Promise原理及实现

315 阅读8分钟

实现前有个规范必须了解: Promise/A+规范

Promise/A+规范

状态

Promise 对象存在以下三种状态:

  • const PENDING = 'PENDING';
  • const RESOLVED = 'RESOLVED'; // 成功
  • const REJECTED = 'REJECTED'; // 失败 状态只能由 PENDING 变为 RESOLVED 或由 PENDING 变为 REJECTED ,且状态改变之后不会在发生变化,会一直保持这个状态。
    Promise的值是指状态改变时传递给回调函数的值
    Promise 对象的 then 方法接受两个参数
promise.then(onFulfilled, onRejected)

参数可选

onFulfilledonRejected 都是可选参数。

  • 如果 onFulfilledonRejected 不是函数,其必须被忽略

onFulfilled 特性

    如果 onFulfilled 是函数:

  • promise 状态变为成功时必须被调用,其第一个参数为 promise 成功状态传入的值( resolve 执行时传入的值)
  • promise 状态改变前其不可被调用
  • 其调用次数不可超过一次

onRejected 特性

    如果 onRejected 是函数:

  • promise 状态变为失败时必须被调用,其第一个参数为 promise 失败状态传入的值( reject 执行时传入的值)
  • promise 状态改变前其不可被调用
  • 其调用次数不可超过一次

多次调用

    then 方法可以被同一个 promise 对象调用多次

  • promise 成功状态时,所有 onFulfilled 需按照其注册顺序依次回调
  • promise 失败状态时,所有 onRejected 需按照其注册顺序依次回调

返回

then 方法必须返回一个新的 promise 对象

promise2 = promise1.then(onFulfilled, onRejected);

因此 promise 支持链式调用

promise1.then(onFulfilled1, onRejected1).then(onFulfilled2, onRejected2);

这里涉及到 Promise 的执行规则,包括“值的传递”和“错误捕获”机制:

1、如果 onFulfilled 或者 onRejected 返回一个值 x ,则运行下面的 Promise 解决过程:[[Resolve]](promise2, x)

  • x 不为 Promise ,则使 x 直接作为新返回的 Promise 对象的值, 即新的onFulfilled 或者 onRejected 函数的参数.
  • xPromise ,这时后一个回调函数,就会等待该 Promise 对象(即 x )的状态发生变化,才会被调用,并且新的 Promise 状态和 x 的状态相同。

下面的例子用于帮助理解:

let promise1 = new Promise((resolve, reject) => {
  setTimeout(() => {
    resolve()
  }, 1000)
})
promise2 = promise1.then(res => {
  // 返回一个普通值
  return '这里返回一个普通值'
})
promise2.then(res => {
  console.log(res) //1秒后打印出:这里返回一个普通值
})
let promise1 = new Promise((resolve, reject) => {
  setTimeout(() => {
    resolve()
  }, 1000)
})
promise2 = promise1.then(res => {
  // 返回一个Promise对象
  return new Promise((resolve, reject) => {
    setTimeout(() => {
     resolve('这里返回一个Promise')
    }, 2000)
  })
})
promise2.then(res => {
  console.log(res) //3秒后打印出:这里返回一个Promise
})

2、如果 onFulfilled 或者onRejected 抛出一个异常 e ,则 promise2 必须变为失败(Rejected),并返回失败的值 e,例如:

let promise1 = new Promise((resolve, reject) => {
  setTimeout(() => {
    resolve('success')
  }, 1000)
})
promise2 = promise1.then(res => {
  throw new Error('这里抛出一个异常e')
})
promise2.then(res => {
  console.log(res)
}, err => {
  console.log(err) //1秒后打印出:这里抛出一个异常e
})

3、如果onFulfilled 不是函数且 promise1 状态为成功(Fulfilled)promise2 必须变为成功(Fulfilled)并返回 promise1 成功的值,例如:

let promise1 = new Promise((resolve, reject) => {
  setTimeout(() => {
    resolve('success')
  }, 1000)
})
promise2 = promise1.then('这里的onFulfilled本来是一个函数,但现在不是')
promise2.then(res => {
  console.log(res) // 1秒后打印出:success
}, err => {
  console.log(err)
})

4、如果 onRejected 不是函数且 promise1 状态为失败(Rejected)promise2必须变为失败(Rejected) 并返回 promise1 失败的值,例如:

let promise1 = new Promise((resolve, reject) => {
  setTimeout(() => {
    reject('fail')
  }, 1000)
})
promise2 = promise1.then(res => res, '这里的onRejected本来是一个函数,但现在不是')
promise2.then(res => {
  console.log(res)
}, err => {
  console.log(err)  // 1秒后打印出:fail
})

了解了规范就可以开始了

实现一个Promise

promise初始化

this.status = PENDING; // 默认是等待态
this.value = undefined; // 成功的原因
this.reason = undefined; // 失败的原因
// 专门存放成功的回调的函数
this.onResolvedCallbacks = [];
// 专门存放失败的回调函数的
this.onRejectedCallbacks = [];

promise里三个重要的方法

resolve(value){
    if(this.state === PENDING){
        this.state = RESOLVED
        this.value = value
        this.onFulfilledCallbacks.forEach((fn)=>fn())
    }
}
reject(reason){
    if(this.state === PENDING){
        this.state = REJECTED
        this.reason = reason
        this.onRejectedCallbacks.forEach((fn)=>fn())
    }
}
then(onFulfilled, onRejected){
    if(typeof onFulfilled !== 'function'){
        // 空处理
        onFulfilled = function(value){
            return value
        }
    }
    if(typeof onRejected !== 'function'){
        // 空处理
        onRejected = function(reason){
            throw reason
        }
    }
    
    if (this.status === RESOLVED) {
        onFulfilled(this.value);
    }
    if (this.status === REJECTED) {
        onRejected(this.reason);
    }
    if (this.status === PENDING) {
        this.onResolvedCallbacks.push(() => {
            onFulfilled(this.value)
        });
        this.onRejectedCallbacks.push(() => {
            onRejected(this.value)
        });
    }
}

resolvereject主要任务就是把成功的值或者失败的原因保存起来,如果then里的onFulfilledonRejected还未执行,则执行then里的方法

如果promise的then方法执行时,状态不是PENDING,则直接执行onFulfilled或者onRejected

onResolvedCallbacksonRejectedCallbacks两个数组主要是异步的时候会用到,因为异步的时候then方法执行还是处于一个PENDING状态,所以要存起来,等promise成功或失败的时候再调用

到这里其实就实现了一个简单的promise,但是这样不能链式调用,要实现链式调用,then返回的必须还是一个promise,且这个then的值是下一个then的参数

promise解决方法

function resolvePromise (promise2,x,resolve,reject){
    // 此方法 为了使所有的promise执行的流程是一样的
    // 1) 不能引用同一个对象 可能会造成死循环
    if(promise2 === x){
        return reject(new TypeError('Chaining cycle detected for promise #<Promise> --'))
    }
    let called;
    if((typeof x ==='object' && x!=null) || typeof x === 'function'){
        try{
            let then = x.then;  // {a:1} 因为then方法 可能使用的getter来定义的
            if(typeof then === 'function'){ // 只能认为他是promise了
                // call 改变this指向 并且让函数执行
                then.call(x,y=>{ // 只取一次 当前promise解析出来的结果可能还是一个promise继续解析直到他是一个普通值为止
                    // 递归解析resolve的值
                    if(called) return;
                    called = true;
                    resolvePromise(promise2,y,resolve,reject)
                },r=>{
                    if(called) return;
                    called = true;
                    reject(r);
                })
            }else{
                // {a:1,then:1}
                resolve(x)
            }
        }catch(e){ //  我取then出错了 在错误中又掉了该promise的成功
            if(called) return
            called = true;
            reject(e); //取值失败 就走到error中
        }
    }else{
        resolve(x)
    }
}

全部代码


const PENDING = 'PENDING';
const RESOLVED = 'RESOLVED'; // 成功
const REJECTED = 'REJECTED'; // 失败

function resolvePromise (promise2,x,resolve,reject){
    // 此方法 为了兼容所有的promise,n个库中间 执行的流程是一样的
    // 尽可能详细 不出错
    // 1) 不能引用同一个对象 可能会造成死循环
    if(promise2 === x){
        return reject(new TypeError('Chaining cycle detected for promise #<Promise> --'))
    }
    let called;
    if((typeof x ==='object' && x!=null) || typeof x === 'function'){
        try{
            let then = x.then;  // {a:1} 因为then方法 可能使用的getter来定义的
            if(typeof then === 'function'){ // 只能认为他是promise了
                // call 改变this指向 并且让函数执行
                then.call(x,y=>{ // 只取一次 当前promise解析出来的结果可能还是一个promise继续解析直到他是一个普通值为止
                    // 递归解析resolve的值
                    if(called) return;
                    called = true;
                    resolvePromise(promise2,y,resolve,reject)
                },r=>{
                    if(called) return;
                    called = true;
                    reject(r);
                })
            }else{
                // {a:1,then:1}
                resolve(x)
            }
        }catch(e){ //  我取then出错了 在错误中又掉了该promise的成功
            if(called) return
            called = true;
            reject(e); //取值失败 就走到error中
        }
    }else{
        resolve(x)
    }
}
class Promise {
    constructor(executor) { // 宏变量
        this.status = PENDING; // 默认是等待态
        this.value = undefined; // 成功的原因
        this.reason = undefined; // 失败的原因
        // 专门存放成功的回调的函数
        this.onResolvedCallbacks = [];
        // 专门存放失败的回调函数的
        this.onRejectedCallbacks = [];
        // 保证只有状态是等待态的时候 才能更改状态
        let resolve = (value) => {
            // 增加
            if(value instanceof Promise){
                value.then(resolve,reject);// 递归解析直到是普通值位置
                return;
            }
            if (this.status === PENDING) {
                this.value = value;
                this.status = RESOLVED;
                // 需要让成功的方法依次执行
                this.onResolvedCallbacks.forEach(fn => fn());
            }
        }
        let reject = (reason) => {
            if (this.status === PENDING) {
                this.reason = reason;
                this.status = REJECTED;
                this.onRejectedCallbacks.forEach(fn => fn());
            }
        } // 执行executor 传入成功和失败
        try {
            executor(resolve, reject); // 立即执行
        } catch (e) {
            reject(e); // 如果内部出错直接将err手动的调用reject方法向下传递
        }
    }
    catch(errCallback){ // catch就是没有成功的then方法
        return this.then(null,errCallback)
    }
    then(onfulfilled, onrejected) {
        onfulfilled = typeof onfulfilled == 'function'?onfulfilled:v=>v;
        onrejected = typeof onrejected == 'function'?onrejected:err=>{throw err}
        // 为了实现链式调用 就创建一个新的promise
        let promise2 = new Promise((resolve, reject) => {
            if (this.status === RESOLVED) {
                setTimeout(() => {
                    try {
                        let x = onfulfilled(this.value);
                        resolvePromise(promise2,x,resolve,reject);
                    } catch (e) { // 一旦执行then方法报错 就走到外层then的错误处理中,调用promise2的reject方法
                        reject(e);
                    }
                }, 0);
            }
            if (this.status === REJECTED) {
                setTimeout(() => {
                    try {
                        let x = onrejected(this.reason);
                        resolvePromise(promise2,x,resolve,reject);
                    } catch (e) {
                        reject(e);
                    }
                }, 0);
            }
            if (this.status === PENDING) {
                // 这时候 executor是肯定有异步逻辑
                this.onResolvedCallbacks.push(() => {
                    // TODO... 切片编程
                    setTimeout(() => {
                        try{
                            let x = onfulfilled(this.value);
                            resolvePromise(promise2,x,resolve,reject);
                        }catch(e){
                            reject(e);
                        }
                    }, 0);
                });
                this.onRejectedCallbacks.push(() => {
                    setTimeout(() => {
                        try{
                            let x = onrejected(this.reason);
                            resolvePromise(promise2,x,resolve,reject);
                        }catch(e){
                            reject(e);
                        } 
                    }, 0);
                })
            }
        })

        return promise2; // 返回这个promise
    }
}
Promise.defer = Promise.deferred = function () { // 稍后继续说 catch
    let dfd = {}
    dfd.promise = new Promise((resolve,reject)=>{
        dfd.resolve = resolve;
        dfd.reject = reject;
    })
    return dfd;
}
module.exports = Promise;

// npm install -g promises-aplus-tests

总结

promise是一个立即执行方法,接收一个方法,这个方法接收两个参数,第一个是resolve,第二个是reject。promise对象里有then方法和catch方法,catch其实就是失败的的then方法。promise内部有三种状态,

const PENDING = 'PENDING';
const RESOLVED = 'RESOLVED'; // 成功
const REJECTED = 'REJECTED'; // 失败

状态只能从PENDING到成功或失败,且改变了就不能再变回去了。状态只能从resolve方法或者reject方法改变。 then方法接收两个参数,第一个是 onfulfilled,第二个是onrejected,onfulfilled方法里接收promise成功时的值,onrejected方法里接收promise失败的原因。 当执行then方法时,如果此时promise的状态是成功或者失败,则直接执行onfulfilled或onrejected方法。如果此时promise的状态是PENDING,则分别把onfulfilled和onrejected存到该promise中的专门存放成功和失败的回调函数的数组中,等待promise触发了resolve或者reject方法时再遍历执行之前存起来的onfulfilled和onrejected。

promise里最难的应该时链式调用的解决方法,由于能链式调用,所以then方法返回的一定还是一个promise对象。 解决链式调用需要一个promise解决方法我们把它叫做resolvePromise,这个方法接收4个参数,(promise2,x,resolve,reject),promise2是then方法返回出来的promise,x是then方法接收的onfulfilled方法或者onrejected的返回值,resolve,reject是promise2的resolve,reject。 在resolvePromise里首先判断x !==promise2,因为这样会导致死循环。然后判断x的类型如果是promise则递归resolvePromise,是普通值则直接resolve(x)。

参考资料

Promise/A+规范

Promise实现原理