Skip to content

promise

MyPromise

js
class MyPromise {
  #status = 'pending'
  #result = undefined
  #handle = []
  constructor(executor){
    const resolve = (data)=>{
      this.#changeState('fullfilled', data)
    }
    
    const reject = (data) =>{
      this.#changeState('rejected', data)
    }
    try{
      // 无法捕获异步错误
      executor(resolve, reject)
    }catch(e){
      reject(e)
    }
  }

  #changeState(state, result){
    if(this.#status !== 'pending') return;
    this.#status = state;
    this.#result = result;
    this.#run()
    
  }
  
  #isPromiseLike(value) {
    return false
  }
  
  #runMicroTask(func) {
    setTimeout(func,0)
  }
  
  #runOne(callback, resolve, reject) {
    if(typeof callback !== 'function') {
      const settled = this.#state === 'fullfilled' ? resolve : reject
      settled(this.#result);
      return
    }
    
    try {
      const data = callback(this.#result);
      if(data instanceof  MyPromise) {
        result.then(reslove, reject);
      }else {
        resolve(data)
      }
    }catch(e) {
      reject(e)
    }
  }
  
  #run(){
    if(this.#status === 'pending') return;
    
    while(this.#handle.length){
      const {onFullfilled, onRejected, resolve, reject} = this.#handle.shift()}
      if(this.#status === 'fullfilled') {
        if(typeof onFullfilled === 'function') {
          try {
            const data = onFullfilled(this.#result)
            resolve(data)
          }catch (e) {
            reject(e)
          }
        }else{
          // 穿透
          resolve(this.#result)
        }
      }else {
        if(typeof onRejected === 'function') {
          try {
            onRejected(this.#result)
          }catch (e) {
            reject(e)
          }
        }else {
          // 穿透
          reject(this.#result)
        }
      }
    }
  }
  
  then(onFullfilled, onRejected){
   
    return new MyPromise((resolve, reject)=>{
      this.#handle.push({
        onFullfilled,
        onRejected,
        resolve,
        reject
      })
      this.#run()
    })
  }
}

var pA = new MyPromise((resolve, reject)=>{
  resolve('foo');
  reject('bar ');
})

pA.then((result)=>{
  console.log(result)
},(reason)=>{
  console.log(reason)
})

Promises/A+规范

由实现者制定,面向实现者的完善、可互操作的 JavaScript 承诺开放标准。

承诺表示异步操作的最终结果。 与许诺交互的主要方式是通过其 then 方法,该方法注册回调以接收许诺的最终值或许诺无法实现的原因。

本规范详细说明了 then 方法的行为,提供了一个可互操作的基础,所有符合 Promises/A+ 标准的许诺实现都可以依赖该基础来提供。

因此,该规范应被视为非常稳定。 尽管 Promises/A+ 组织可能会偶尔对本规范进行一些向后兼容的小修改,以解决新发现的角落情况, 但我们只有在经过仔细考虑、讨论和测试后,才会整合大的或向后不兼容的修改。

从历史上看,Promises/A+ 阐明了早期 Promises/A 提议中的行为条款,将其扩展到涵盖事实上的行为,并省略了规范不足或有问题的部分。

最后,Promises/A+ 的核心规范并不涉及如何创建、履行或拒绝承诺,而是专注于提供一种可互操作的方法。 配套规范的未来工作可能会涉及这些主题。

1.术语

1.1. "promise"是具有 then 方法的对象或函数,其行为符合本规范。

1.2. "thenable"是定义了 then 方法的对象或函数。

1.3. "value"是任何合法的 JavaScript 值(包括未定义值、thenable 或 promise)。

1.4. "exception"是使用 throw 语句抛出的值。

1.5. "reason"是表示拒绝接受承诺的原因的值。

2.允诺

2.1 状态要求

允诺必须处于三种状态之一:待定、已履行或已拒绝。

2.1.1. 待定时,允诺:

   2.1.1.1 可过渡到已履行或已拒绝状态。

2.1.2. 履行时,允诺:

  2.1.2.1. 不得过渡到任何其他状态。

  2.1.2.2. 必须有一个值,且该值不得更改。

2.1.3. 拒绝时,允诺:

  2.1.3.1. 不得过渡到任何其他状态。

  2.1.3.2. 必须有一个理由,且该理由不得更改。

这里,"不得更改"是指不变的身份(即 ===),但并不意味着深度不变性。

2.2. then 方法

承诺必须提供一个 then 方法来访问其当前或最终值或原因。

承诺的 then 方法接受两个参数:

js
promise.then(onFulfilled, onRejected)

2.2.1. onFulfilled 和 onRejected 都是可选参数:

  2.2.1.1. 如果 onFulfilled 不是函数,则必须忽略。

  2.2.1.2. 如果 onRejected 不是函数,则必须忽略。

2.2.2. 如果 onFulfilled 是函数:

  2.2.2.1. 它必须在承诺实现后调用,并以承诺的值作为其第一个参数。

  2.2.2.2. 在拒绝许诺之前,不得调用它。

  2.2.2.3. 不得多次调用。

2.2.3. 如果 onRejected 是函数:

  2.2.3.1. 必须在允诺被拒绝后调用,并以允诺的理由作为第一个参数。

  2.2.3.2. 在允诺被拒绝前不得调用。

  2.2.3.3. 不得多次调用。

2.2.4. onFulfilled 或 onRejected 必须在执行上下文堆栈只包含平台代码时才能调用。 [3.1]

2.2.5. onFulfilled 和 onRejected 必须作为函数调用(即不带 this 值)。 [3.2]

2.2.6. then可对同一承诺多次调用。

  2.2.6.1. 如果/当承诺被履行时,所有相应的 onFulfilled 回调必须按照对 then 的调用顺序执行。

  2.2.6.2. 如果/当承诺被拒绝时,所有相应的 onRejected 回调必须按照对 then 的调用顺序执行。

2.2.7. then必须返回一个承诺[3.3]。

js
 promise2 = promise1.then(onFulfilled, onRejected);

  2.2.7.1. 如果 onFulfilled 或 onRejected 返回值 x,则运行允诺解析过程[[Resolve]](promise2, x)。

  2.2.7.2. 如果 onFulfilled 或 onRejected 抛出异常 e,则必须以 e 为理由拒绝接受 promise2。

  2.2.7.3. 如果 onFulfilled 不是函数,且 promise1 被履行,则必须以与 promise1 相同的值履行 promise2。

  2.2.7.4. 如果 onRejected 不是函数,且 promise1 被拒绝接受,则必须以与 promise1 相同的理由拒绝接受 promise2。

2.3. 允诺解析过程

允诺解析过程是一个抽象操作,它的输入是一个允诺和一个值,我们将其标记为[[Resolve]](promise, x)。 如果 x 是可允诺的,那么在假设 x 的行为至少有点像允诺的情况下,它将尝试使允诺采用 x 的状态。

对 thenable 的这种处理方法允许 promise 实现互操作,只要它们暴露了一个符合 Promises/A+ 的 then 方法。 它还允许 Promises/A+ 实现用合理的 then 方法 "同化 "不符合要求的实现。

要运行 [[Resolve]](promise, x),请执行以下步骤:

2.3.1. 如果 promise 和 x 指向同一个对象,则以 TypeError 作为理由拒绝 promise。

2.3.2. 如果 x 是一个 promise,则采用它的状态 [3.4]:

  2.3.2.1. 如果 x 是 pending,则 promise 必须保持 pending 状态,直到 x 被履行或拒绝。

  2.3.2.2. 如果/当 x 被履行时,用相同的值履行 promise。

  2.3.2.3. 如果/当 x 被拒绝时,用相同的理由拒绝 promise。

2.3.3. 否则,如果 x 是一个对象或函数,

2.3.3.1. 则让 then 成为 x.then。 [3.5]
2.3.3.2. 如果检索 x.then 属性会导致抛出异常 e,则以 e 为理由拒绝许诺。
2.3.3.3. 如果 then 是一个函数,则以 x 为 this、第一个参数 resolvePromise 和第二个参数 rejectPromise 调用该函数,其中:

  2.3.3.3.1. 如果/当 resolvePromise 被调用且值为 y 时,运行 [[Resolve]](promise, y)

  2.3.3.3.2. 如果/当 rejectPromise 被调用且理由为 r 时,以 r 为理由拒绝许诺。

  2.3.3.3.3. 如果同时调用了 resolvePromise 和 rejectPromise,或者对同一个参数进行了多次调用,则以第一次调用为准,以后的调用将被忽略。

  2.3.3.3.4. 如果调用 then 会抛出异常 e,

    2.3.3.3.4.1. 如果已经调用了 resolvePromise 或 rejectPromise,则忽略它。

    2.3.3.3.4.2. 否则,以 e 为理由拒绝接受 promise。

2.3.3.4. 如果 then 不是函数,则用 x 来履行 promise。

2.3.4. 如果 x 不是对象或函数,则用 x 履行承诺。

如果使用参与循环 thenable 链的 thenable 来解决一个 promise,从而使 [[Resolve]](promise, thenable) 的递归性质最终导致 [[Resolve]](promise, thenable) 被再次调用,那么按照上述算法将导致无限递归。 我们鼓励(但不要求)实现检测这种递归,并以告知的 TypeError 作为拒绝 promise 的理由。 [3.6]

3.后记

3.1. 这里的 "平台代码 "是指引擎、环境和承诺实现代码。 在实践中,这一要求可确保 onFulfilled 和 onRejected 在调用它们的事件循环转折之后异步执行,并使用新的堆栈。 这可以通过 setTimeout 或 setImmediate 等 "宏任务 "机制或 MutationObserver 或 process.nextTick 等 "微任务 "机制来实现。 由于 promise 实现被视为平台代码,因此它本身可能包含一个任务调度队列或 "蹦床",处理程序就在其中被调用。

3.2. 也就是说,在严格模式下,这在它们内部是未定义的;而在粗放模式下,它将是全局对象。

3.3. 实现可以允许 promise2 === promise1,前提是实现满足所有要求。 每个实现都应记录它能否产生 promise2 === promise1 以及在什么条件下产生。

3.4. 一般来说,只有当 x 来自当前的实现时,我们才会知道它是一个真正的承诺。 本条款允许使用特定于实现的方法来采用已知符合性承诺的状态。

3.5. 首先存储 x.then 的引用,然后测试该引用,最后调用该引用,这一过程避免了对 x.then 属性的多次访问。 这种预防措施对于确保访问者属性的一致性非常重要,因为访问者属性的值可能会在两次检索之间发生变化。

3.6. 实现不应对 thenable 链的深度设置任意限制,并假定超出任意限制后递归将是无限的。 只有真正的循环才会导致 TypeError;如果遇到不同 thenable 的无限链,那么永远递归才是正确的行为。

参考资料