Skip to content

Terminology

  1. “promise” is an object or function with a then method whose behavior conforms to this specification.
  2. “thenable” is an object or function that defines a then method.
  3. “value” is any legal JavaScript value (including undefined, a thenable, or a promise).
  4. “exception” is a value that is thrown using the throw statement.
  5. “reason” is a value that indicates why a promise was rejected.

  1. promise是一个拥有 then⽅法的对象或者是函数,其行为遵循本规范
  2. thenable是一个定义了then方法的对象或者函数
  3. value 是任意类型的JavaScript值(包括undefined, thenabel或promise)
    1. 是promise状态成功时的值,也就是resolve的参数
  4. exception 是由 throw 抛出的异常值
  5. reason 是promise状态失败时的值,也就是reject的参数,表示拒绝的原因

Promise States

A promise must be in one of three states: pending, fulfilled, or rejected.

  1. When pending, a promise:
    1. may transition to either the fulfilled or rejected state.
  2. When fulfilled, a promise:
    1. must not transition to any other state.
    2. must have a value, which must not change.
  3. When rejected, a promise:
    1. must not transition to any other state.
    2. must have a reason, which must not change.
  • Here, “must not change” means immutable identity (i.e. ===), but does not imply deep immutability.

有三种状态, 他们之间的关系

  1. pending

    1. 初始状态,可改变
    2. 在resolve和reject前都处于这个状态
    3. 通过resolve -> fulfilled状态
    4. 通过reject -> rejected状态
  2. fulfilled

    1. 最终态, 不可变
    2. 一个promise被resolve之后会变成这个状态
    3. 必须拥有一个value
  3. rejected

    1. 最终态,不可变
    2. 一个promise被reject之后会变成这个状态
    3. 必须拥有reason

总结:

  • pending -> resolve(value) -> fulfilled
  • pending -> reject(reason) -> rejected

then

A promise 必须提供一个then方法, 用来访问当前或最终的valuereason.

js
promise.then(onFulfilled, onRejected)
  1. onFulfilledonRejected 都是可选参数

    1. onFulfilled必须是函数类型, 如果传入的不是函数, 应该被忽略
    2. onRejected必须是函数类型, 如果传入的不是函数, 应该被忽略
  2. 如果onFulfilled是一个函数

    1. 在promise状态由pending变成fulfilled之后, 应该调用onFulfilled, 参数是value.(onFulfilled执行时机?)
      • 换句话说,onFulfiled必须在promise is fulfilled之后调用, value值作为它的第一个参数
    2. 在promise变成fulfilled之前, 不应该调用onFulfilled.
    3. 只能调用一次 (怎么实现只调用一次?)
  3. 如果onRejected是一个函数

    1. 在promise变成 rejected 时, 调用onRejected, 参数是reason.
    2. 在promise变成 rejected 之前, 不应该调用 onRejected.
    3. 只能被调用一次
  4. onFulfilled 和 onRejected 应该是微任务阶段执行. (此处参考规范)

    • 实现promise的时候, 如何去生成微任务?
  5. onFulfilled 和 onRejected 必须作为函数被调用(没有this值)

    • must be called as functions (i.e. with no this value)
  6. 同一个promise上的then方法可以被调用多次

    1. promise状态 变成 fulfilled 后, 所有相应的onFulfilled回调都必须按照他们原始调用then的顺序执行
      • promise.then(onFulfilled, onRejected).then(onFulfilled1), 按照 .then 的顺序执行
    2. promise状态 变成 rejected 后, 所有相应的onRejected回调都必须按照他们原始调用then的顺序执行
      • 所以在实现的时候需要⼀个数组来存放多个onRejected的回调
  7. then必须返回一个promise

    js
    const promise2 = promise1.then(onFulfilled, onRejected)
    1. 如果onFulfilledonRejected 执行结果为x(return a value x), 调用[[Resolve]](promise2, x)
      • [[Resolve]] -> resolvePromise方法
    2. 如果 onFulfilled 或者 onRejected 执⾏时抛出异常e, promise2必须以e作为reason被rejected
    3. 如果 onFulfilled 不是一个函数,并且promise1 is fulfilled(已完成), promise2 必须用promise1的相同的value触发fulfilled
    4. 如果 onRejected 不是一个函数, 并且promise1 is rejected(被拒绝), promise2 必须用promise1的相同的reason 触发rejected

The Promise Resolution Procedure

promise处理过程是一个抽象操作,它以一个promise和一个值作为输入,我们表示为[[Resolve]](promise, x).

如果xthen方法(thenable),并且看上去像一个Promise,[[Resolve]]解决程序尝试使promise接受x的状态(make promise adopt the state of x); 否则, 用x的值来执行promise. (it fulfills promise with the value x.)

这种对thenables的处理使得Promis的实现更具通用性: 只要暴露一个遵循Promise/A+规范的then方法即可.

[[Resolve]](promise, x)

需要执行以下步骤:

  1. 如果promisex指向同一对象(refer to the same object),以TypeError作为reason拒绝promise.(reject promise with a TypeError as the reason.)
  2. 如果x是一个promise,采用它的状态:
    1. 如果x处于等待状态,promise需保持为等待状态,直到x已完成或被拒绝( is fulfilled or rejected)
    2. 如果x处于执行态, 用相同的值执行promise (If/when x is fulfilled, fulfill promise with the same value)
    3. 如果x处于拒绝态, 用相同的reason拒绝promise
  3. 否则,如果x是一个对象或函数: (不常见)
    1. Let then be x.then [3.5]
    2. 如果取x.then的值时(retrieving)抛出异常e(exception e), 则以ereason拒绝promise
    3. 如果then是函数,让x做为this调用它,then方法传递两个回调函数作为参数,第一个参数叫做resolvePromise,第二个参数叫做rejectPromise
      • resolvePromise以值y为参数被调用, 运行[[Resolve]](promise, y)
      • rejectPromise以拒因(reason) r为参数被调用时,以r拒绝promise
      • 如果 resolvePromiserejectPromise 均被调⽤,或者被同⼀参数调⽤了多次,则优先采⽤⾸次调⽤并忽略其他的调⽤
      • 如果调⽤ then ⽅法抛出了异常 e
        1. 如果 resolvePromise 或 rejectPromise 已经被调⽤,则忽略
        2. 否则, 以e作为reason拒绝promise (reject promise with e as the reason.)
    4. 如果then不是函数,以 x 为参数将 promise 变为已完成状态 (fulfill promise with x)
  4. 如果x不是对象或函数, 以 x 为参数将 promise 变为已完成状态 (fulfill promise with x). - 重要且常见!

如果promise用一个循环的thenable链解决,由于[[Resolve]](promise, thenalbe)的递归特性,最终将导致[[Resolve]](promise, thenable)被再次调用,上述算法将会导致无限递归, 鼓励(但不是必须)实现检测这种递归,并以TypeError作为拒绝promise的理由.

Notes

3.5 这个过程首先存储对 x.then 的引用,然后测试该引用,然后调用该引用,从而避免对 x.then 属性的多次访问;这种预防措施对于确保访问属性的一致性非常重要,访问属性的值可能在检索之间(between retrievals 读取)发生变化

Tips

以上, 按着我的理解, 将Promises/A+规范基本整体翻译了一遍, 除了一些Notes注解部分未翻译, 大家可以参考规范自行翻译. 下面, 我们来自己实现一个Promises/A+.


一步步实现一个Promise

  1. const promise = new Promise() 代表 Promise是一个构造函数或者class
  2. 定义三种状态.
  3. 初始化状态
  4. resolve 和 reject 方法
    1. 这两个方法要更改status, 从pending变成fulfilled / rejected
    2. 入参分别是 value / reason
  5. 对于实例化promise时的入参处理
    1. 入参是一个函数, 接收 resolve reject 两个参数
    2. 初始化promise的时候, 就要同步执行这个函数, 并且有任何的报错都要通过reject抛出去
  6. then 方法
    js
    const promise2 = promise1.then(onFulfilled, onRejected)
    1. then 接收两个可选参数, onFulfilled 和 onRejected
    2. 检查并处理参数, 如果参数不是函数, 就忽略
    3. 根据当前promise的状态, 调用不同的函数
    4. 首先我们要拿到所有的回调, 新建两个数组, 分别存储成功和失败的回调, 调用then的时候, 如果还是pending就存入数组.
    5. 在status发生变化的时候, 执行回调, 用到getter setter, 监听status的变化, 在发生变化的时候来做对应的操作.
  7. then 的返回值
    1. 如果 onFulfilled 或者 onRejected 抛出一个异常 e ,那么新的promise2必须reject e
    2. 返回值必须是一个promise
    3. 如果 onFulfilled 不是函数, 且 promise1 成功执行, 那么promise2必须返回同样的状态(成功执行)和value.
    4. 如果 onRejected 不是函数, 且promise1 拒绝执行, promise2必须返回同样的状态(拒绝)和reason.
    5. 如果 onFulfilled 或者 onRejected 返回一个值 x, 运行resolvePromise方法
js
const PENDING = 'pending'
const FULFILLED = 'fulfilled'
const REJECTED = 'rejected'

class MyPromise {

    onFulfilledCallbacks = [] // 实例属性
    onRejectedCallbacks = []
    // _status = PENDING
    constructor(executor){
        this.status = PENDING
        this.value = undefined
        this.reason = undefined
        // this.onFulfilledCallbacks = []  写上面和这里是一样的
        // this.onRejectedCallbacks = [] // 实例属性
        try {
            executor(this.resolve.bind(this),this.reject.bind(this))
        } catch (e) {
            this.reject(e)
        }
    }

    // 利用getter, setter监听status改变  其实可以不用这里监听
    // get status() {
    //     return this._status
    // }

    // set status(newStatus) {
    //     this._status = newStatus
    //     switch (newStatus) {
    //         case FULFILLED: {
    //             this.FULFILLED_CALLBACK_LIST.forEach(callback => {
    //                 callback(this.value);
    //             });
    //             break;
    //         }
    //         case REJECTED: {
    //             this.REJECTED_CALLBACK_LIST.forEach(callback => {
    //                 callback(this.reason);
    //             });
    //             break;
    //         }
    //     }
    // }

    resolve(value) {
        if (this.status === PENDING) {
            this.value = value;
            this.status = FULFILLED;
            this.onFulfilledCallbacks.forEach(cb=>cb(this.value));
        }
    }

    reject(reason) {
        if (this.status === PENDING) {
            this.reason = reason;
            this.status = REJECTED;
            this.onRejectedCallbacks.forEach(cb=>cb(this.reason));
        }
    }

    then(onFulfilled, onRejected) {
        const realOnFulfilled = this.isFunction(onFulfilled) ? onFulfilled : value => value
        const realOnRejected = this.isFunction(onRejected) ? onRejected : reason => {throw reason}
        // 为了链式调用这里直接创建一个 MyPromise,并在后面 return 出去
        const promise2 = new MyPromise((resolve,reject)=>{
             // fulfilled处理
             const fulfilledMicrotask = () => {
                 // 创建一个微任务等待 promise2 完成初始化
                 queueMicrotask(() => {
                     try {
                         // 获取成功回调函数的执行结果
                         const x = realOnFulfilled(this.value);
                         // 传入 resolvePromise 集中处理
                         this.resolvePromise(promise2, x, resolve, reject);
                     } catch (e) {
                         reject(e)
                     }
                 })
             }
             // rejected处理
             const rejectedMicrotask = () => {
                 queueMicrotask(() => {
                     try {
                         // 调用失败回调,并且把原因返回
                         const x = realOnRejected(this.reason);
                         this.resolvePromise(promise2, x, resolve, reject);
                     } catch (e) {
                         reject(e);
                     }
                 })
             }
             // 判断状态
             switch (this.status) {
                 case FULFILLED: {
                     fulfilledMicrotask();break;
                 }
                 case REJECTED: {
                     rejectedMicrotask();break;
                 }
                 case PENDING: {
                     this.onFulfilledCallbacks.push(fulfilledMicrotask)
                     this.onRejectedCallbacks.push(rejectedMicrotask)
                 }
             }
             
         })
         // then 返回promise2 实现链式调用
         return promise2
    }  // end then

    resolvePromise(promise2, x, resolve, reject){
        /* 2.3.1 */
        if (promise2 === x) {
            return reject(new TypeError('Chaining cycle detected for promise #<Promise>'));
        }
        /* 2.3.2 */
        if (x instanceof MyPromise) {
            // 如果 x 为 Promise ,则使 promise2 接受 x 的状态
            // 也就是继续执行x,如果执行的时候拿到一个y,还要继续解析y
            // 这个if跟下面判断then然后拿到执行其实重复了,可有可无
            x.then((y) => {
                this.resolvePromise(promise2, y, resolve, reject);
            }, reject);
        }
        /* 2.3.3 */
        if (x!=null&&(typeof x === 'object' || typeof x === 'function')) {  

            let then = null;
            try {
                /* 2.3.3.1 */
                then = x.then; // 把 x.then 赋值给 then
            } catch (e) {
                /* 2.3.3.2 */
                return reject(e);
            }
    
            /* 2.3.3.3 */
            if (typeof then === 'function') {
                let called = false; // 标记是否调用过
                // 将 x 作为函数的作用域 this 调用
                // 传递两个回调函数作为参数,第一个参数叫做 resolvePromise ,第二个参数叫做 rejectPromise
                try {
                    then.call(
                        x,
                        // 如果 resolvePromise 以值 y 为参数被调用,则运行 resolvePromise
                        (y) => {
                            /* 2.3.3.3.1 */ 
                            // 需要有一个变量called来保证只调用一次.
                            if (called) return;
                            called = true; /* 2.3.3.3.3 */ 
                            this.resolvePromise(promise2, y, resolve, reject);
                        },
                        // 如果 rejectPromise 以据因 r 为参数被调用,则以据因 r 拒绝 promise
                        (r) => {
                            /* 2.3.3.3.2 */ 
                            if (called) return;
                            called = true; /* 2.3.3.3.3 */ 
                            reject(r);
                        })
                /* 2.3.3.3.4 */     
                } catch (e) {
                    // 如果调用 then 方法抛出了异常 e:
                    if (called) return;
                    // 否则以 e 为据因拒绝 promise
                    reject(e);
                }
            }
             /* 2.3.3.4 如果 then 不是函数,以 x 为参数执行 promise*/ 
            if (typeof then !== 'function') resolve(x)
        }
        /* 2.3.4 // 如果 x 不为对象或者函数,以 x 为参数执行 promise*/
        if (typeof x !== 'object' && typeof x !=='function' || x === null) resolve(x)
    } 
    // resolve 静态方法
    static resolve (value) {
        // 如果传入 MyPromise 就直接返回
        if (value instanceof MyPromise) return value
        // 普通值,创建promise对象
        return new MyPromise(resolve => resolve(value))
    }
    // reject 静态方法
    static reject (reason) {
        return new MyPromise((resolve, reject) => {
            reject(reason)
        })
    }
    // 工具函数, 判断function
    isFunction(value) {
        return typeof value === 'function';
    }
}


// 测试
// npm install promises-aplus-tests -D
MyPromise.deferred = function() {
    let result = {}
    result.promise = new MyPromise(function(resolve,reject){
        result.resolve = resolve
        result.reject = reject
    })
    return result
}

module.exports = MyPromise

测试PromiseA+

  1. 安装
bash
npm install promises-aplus-tests -D
  1. 代码中加入deferred
js
MyPromise.deferred = function() {
    let result = {}
    result.promise = new MyPromise(function(resolve,reject){
        result.resolve = resolve
        result.reject = reject
    })
    return result
}

module.exports = MyPromise
  1. package.json中配置启动命令
json
{
  "name": "promise",
  "version": "1.0.0",
  "description": "my promise",
  "main": "MyPromise.js",
  "scripts": {
    "test": "promises-aplus-tests myPromiseA+"
  },
  "author": "ITEM",
  "license": "ISC",
  "devDependencies": {
    "promises-aplus-tests": "^2.1.2"
  }
}
  1. 开始测试
bash
npm run test

Promise-API

实例方法

  • Promise.prototype.then()
  • Promise.prototype.catch()
  • Promise.prototype.finally()

静态方法

  • Promise.all()
  • Promise.race()
  • Promise.allSettled()
  • Promise.any()
  • Promise.resolve()
  • Promise.reject()

ES6官方Promise还有其他一些API,不在Promises/A+规范里, 下一篇文章中我们再看看怎么自己实现其他一些方法

Acknowledgments

  1. PromiseA+官方
  2. PromiseA+译
  3. github-then/promise
  4. github-es6-promise
  5. 从一道让我失眠的 Promise 面试题开始,深入分析 Promise 实现细节 good!
  6. 手写一个Promise/A+,完美通过官方872测试 good!
  7. 面试官:“你能手写一个 Promise 吗” good!
  8. Promise的源码实现(完美符合Promise/A+规范) good!
  9. BAT前端经典面试问题:史上最最最详细的手写Promise教程 good!
  10. 史上最详细手写promise
  11. Promise实现原理
  12. bilibili-Promise
  13. promise源码
  14. promise实现 es6
  15. 让你彻底掌握es6 Promise的八段代码