Hey, 你的Promise

Promise 是异步编程的一种解决方案,比传统的解决方案——回调函数和事件——更合理和更强大。它由社区最早提出和实现,ES6 将其写进了语言标准,统一了用法,原生提供了Promise对象。 @阮老师

先来看看promise的几种用法:

  • Promise.prototype.then:then方法有两个参数,第一个参数是Promise成功时执行的callback,第二个是Promise失败时执行的callback,then方法返回的是一个新的Promise实例
    new Promise((resolve, reject) => resolve()).then(() => {
        console.log('success');
    }, () => {
        console.log('err')
    })
复制代码
  • Promise.prototype.catch:用于指定发生错误时的回调函数
如果没有指定reject函数,最后就会执行catch函数

new Promise((resolve, reject) => {
    reject('err');
})
.then()
.catch(e => {
    console.log(e); // => Error: err
})
复制代码
  • Promise.resolve:定义在Promise类本身的方法,可以通过Promise.resolve()调用,相当于直接将状态改为成功态
Promise.resolve('hahaha').then(data => {
    console.log(data); // => 'hahaha'
})
复制代码
  • Promise.reject:定义在Promise类本身的方法,可以通过Promise.reject()调用,相当于直接将状态改为失败
Promise.reject('hahaha').then(data => {
    console.log(data); // => 'hahaha'
})
复制代码
  • Promise.prototype.all:将多个Promise执行的结果放到一个数组中返回
let promise1 = new Promise((resolve, reject) => {
    setTimeout(() => {
          resolve('promise1');
    }, 1500);
})

let promise2 = new Promise((resolve, reject) => {
    setTimeout(() => {
          resolve('promise2');
    }, 2000);
})

Promise.all([promise1, promise2]).then(data => {
    console.log(data); // => ["promise1", "promise2"]
})
复制代码
  • ...

下面我们自己来实现一个符合 PromiseA+规范 的Promise

我们先来简单的用一下promise:

console.log(1)
new Promise(() => {
    console.log(2)
});
console.log(3)
复制代码

因为我们都知道promise是异步的,所以按理会依次输出 1,3,2 然后我们运行之后发现 它依次输出的是 1,2,3 当我们使用then的时候:

console.log(1)
Promise.resolve().then(() => {
    console.log(2);
})
console.log(3)

复制代码

再次运行,发现结果和我们之前想的是一样的,会依次输出 1,3,2 这是因为promisecallback是立即执行的,只有then方法是异步的

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

  1. promise必须有三个状态 pending(等待态) fulfilled(成功态) rejected(失败态)
    • 当 state 是 pending 的时候
      • 可以将当前状态改变成其他状态(fulfilled or rejected)
    • 当 state 是 fulfilled 的时候
      • 不能转换成其他状态
      • 必须有一个value(成功之后的值)
    • 当 state 是 rejected 的时候
      • 不能转换成其他状态
      • 必须有一个reason(失败的原因)
class Promise {
    constructor(executor) {
        // 每一个promise实例都有自己的三个状态,我们用一个变量来保存当前的状态
        this.status = 'pending';
        // 用来保存执行成功时的值
        this.value;
        // 用来保存执行失败时的原因
        this.reason;
        
        // 用来将当前状态改变为成功态的方法
        let resolve = val => {
            // 只有当前状态是`pending` 才可以改变状态和值
            if (this.status === 'pending') {
                this.value = val;
                this.status = 'fulfilled';
            }
        }
        
        // 用来将当前状态改变为失败态的方法
        let reject = reason => {
            // 只有当前状态是`pending` 才可以改变状态和值
            if (this.status === 'pending') {
                this.reason = reason;
                this.status = 'rejected';
            }
        }
        
        // 执行executor可能会直接抛出异常,我们用try catch包起来
        try {
           executor(resolve, reject); 
        } catch (e) {
            // 如果抛出异常,我们直接将状态改为失败态
            reject(e);
        }
    }
}
复制代码
  1. then方法

一个promise必须提供一个then方法去获取当前的或最终的value or reason

promise的then方法有两个参数,分别是成功时执行的方法onFulfilled和失败时执行的方法onRejected

  • onFulfilledonRejected都是可选的参数
    • 如果没传,我们需要设置一个默认方法
  • 如果onFulfilled是一个function
    • 它必须在当前promise的状态变成fulfilled之后被调用,将promise的value(成功之后的值)作为他的第一个参数
    • 在状态是fulfilled之前不能调用它
    • 只能被调用一次
  • 如果onRejected是一个function
    • 它必须在当前promise的状态变成rejected之后被调用,将promise的reason(失败的原因)作为他的第一个参数
    • 在状态是rejected之前不能调用它
    • 只能被调用一次
promise.then(onFulfilled, onRejected)
复制代码
class Promise {
    constructor(executor) {...}
    
    then(onFulfilled, onRejected) {
        
        // 如果当前状态是成功态,我们就执行成功的回调,并将存起来的成功的值value传过去
        if (this.status === 'fulfilled') {
            onFulfilled(this.value);
        }
        
        // 如果当前状态是失败态,我们就执行失败的回调,并将存起来的失败原因reason传过去
        if (this.status === 'rejected') {
            onRejected(this.reason);
        }
    }
}
复制代码

好了,我们现在已经可以简单的测试一下了

new Promise((resolve, reject) => {
    resolve('完美');
}).then(data => {
    console.log(data); // => 完美
})
复制代码

完美,但是那么问题来了,如果我们的 resolvereject 是异步的呢?

new Promise((resolve, reject) => {
    setTimeout(() => {
        resolve('完美');
    }, 1000);
}).then(data => {
    console.log(data); 
}, err => {
    console.log(err);
})
复制代码

运行之后我们发现什么都没有输出,onFulfilledonRejected 都没有打印东西,那说明他们都没有执行,let me think think...

这是因为如果我们的 resolvereject是异步的,当我们的then执行的时候,状态还没有改变,还是pending状态,所以当然什么都不会执行。我们可以先把 onFulfilledonRejected 存起来,等状态改变的时候依次执行对应的callback。A: 这难道是?B: 没错,这就是订阅发布模式。下面我们来改写一下:

class Promise {
    constructor(executor) {
        this.status = 'pending';
        this.value;
        this.reason;
        
        // 用来保存成功时执行的回调函数
        this.onSuccessCallback = [];
        
        // 用来保存失败时执行的回调函数
        this.onErrorCallback = [];
        
        let resolve = val => {
            if (this.status === 'pending') {
                this.value = val;
                this.status = 'fulfilled';
                
                // 状态改变时 依次执行成功的回调
                this.onSuccessCallback.forEach(fn => fn());
            }
        }
        
        let reject = reason => {
            if (this.status === 'pending') {
                this.reason = reason;
                this.status = 'rejected';
                
                // 状态改变时 依次执行失败的回调
                this.onErrorCallback.forEach(fn => fn());
            }
        }
        
        // 执行executor可能会直接抛出异常,我们用try catch包起来
        try {
           executor(resolve, reject); 
        } catch (e) {
            // 如果抛出异常,我们直接将状态改为失败态
            reject(e);
        }
    }
    
    then(onFulfilled, onRejected) {
        
        if (this.status === 'fulfilled') {
            onFulfilled(this.value);
        }
        
        if (this.status === 'rejected') {
            onRejected(this.reason);
        }
        
        if (this.status === 'pending') {
        
            // 将成功回调和失败回调都存起来,等待状态改变,再依次执行对应的方法
            this.onSuccessCallback.push(() => {
                onFulfilled(this.value);
            });
            
            this.onErrorCallback.push(() => {
                onRejected(this.reason);
            });
        }
    }
}
复制代码
  • onFulfilledonRejected都是异步调用(微任务)

  • onFulfilledonRejected必须作为一个函数被执行

  • then方法必须返回一个promise

    promise2 = promise1.then(onFulfilled, onRejected)
    复制代码
    • 如果onFulFilled or onRejected返回了一个值x,运行promise解析程序
    • 如果onFulfilled or onRejected抛出了一个异常epromise2的状态必须是rejected,并将e作为onRejected的参数
    • 如果onFulfilled不是一个function并且promise1fulfilledpromise2必须也是fulfilled并且使用和promise1相同的value
    • 如果onRejected不是一个function并且promise1rejectedpromise2必须也是rejected并且使用和promise1相同的reason

    我们用promise的时候经常promise.then().then().then() ...这种写法叫链式调用,那怎么样才能继续调用then方法呢,规范规定then方法必须返回一个promise实例,这样就可以实现链式调用了

    class Promise {
        contructor() {...}
        
        then(onFulfilled, onRejected) {
            // 如果onFulfilled和onFulfilled 不是一个函数,我们给一个默认值
            onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : val => val;
            onRejected = typeof onRejected === 'function' ? onRejected : err => { throw err };
            // 我们将所有东西都包到一个promise实例中,最后返回这个实例,这样就可以实现链式调用
            let promise2;
            // `onFulfilled` 和 `onRejected`都是异步调用,我们先用一个定时器实现异步调用
            promise2 = new Promise((resolve, reject) => {
    
                if (this.status === 'fulfilled') {
                    setTimeout(() => {
                        try {
                            // 有一个返回值x,运行解析函数resolvePromise
                            let x = onFulfilled(this.value);
                            resolvePromise(promise2, x, resolve, reject)
                        } catch (e) {
                            reject(e);
                        }
                    }, 0);
                }
                    
                if (this.status === 'rejected') {
                    setTimeout(() => {
                        try {
                            // 有一个返回值x,运行解析函数resolvePromise
                            let x = onRejected(this.reason);;
                            resolvePromise(promise2, x, resolve, reject)
                        } catch (e) {
                            reject(e);
                        }
                    }, 0);
                }
            
                if (this.status === 'pending') {
        
                    // 将成功回调和失败回调都存起来,等待状态改变,再依次执行对应的方法
                    this.onSuccessCallback.push(() => {
                        setTimeout(() => {
                            try {
                                // 有一个返回值x,运行解析函数resolvePromise
                                let x = onFulfilled(this.value);
                                resolvePromise(promise2, x, resolve, reject)
                            } catch (e) {
                                reject(e);
                            }
                        }, 0);
                    });
                
                    this.onErrorCallback.push(() => {
                        setTimeout(() => {
                            try {
                                // 有一个返回值x,运行解析函数resolvePromise
                                let x = onRejected(this.reason);;
                                resolvePromise(promise2, x, resolve, reject)
                            } catch (e) {
                                reject(e);
                            }
                        }, 0);
                    });
                }
            });
            return promise2;
        }
    }
    复制代码

    这里可能就会有疑惑了,如果有一个返回值x就运行promise解析程序resolvePromise,这是什么鬼?

    我们先来看看规范:

    promise的解析程序是将promise2x作为参数的函数

  • 如果promise2x是一个promise,那么抛出一个TypeError

  • 如果x是一个objectfunction

    • 使用变量then存储x.then
    • 如果x.then会抛出异常e,调用promise的reject,并将e作为它的参数
    • 如果then是一个function,使用call把它的this指向x,它的第一个参数是resolvePromise,第二个参数是rejectPromise:
      • resolvePromisey值调用的时候,继续执行解析程序
      • rejectPromise执行的时候,调用promise的reject并将将失败原因r作为它的参数
    • 如果then不是一个object or function,调用promise的resolve并将x作为它的参数
  • 如果x不是一个object or function,调用promise的resolve并将x作为它的参数

总结下来就两点:

  1. 如果promise2x相等,就抛出一个TypeError,我们先来看一下

    let p = new Promise((resolve, reject) => {
        // 返回当前promise实例
        return p;
    });
    
    p.then(data => {
        console.log(data);
    }, err => {
        console.log(err);
    });
    复制代码

    运行上面代码,我们会发现promise抛出了一个异常,他告诉我们TypeError: Chaining cycle detected for promise,这是因为p的成功还是失败取决于自己,自己再等待自己的执行结果,所以他既不会成功也不会失败

    1. 如果onFulFilled or onRejected返回了一个值x,运行promise解析程序resolvePromise

    返回值x有可能是一个常量,对象,也有可能是一个promise,这个程序的作用就是如果 x 是一个promise,那就将 x 一直解析到常量位置

    let p = new Promise((resolve, reject) => {
        resolve(new Promise((resolve, reject) => {
            resolve(1111);
        }))
    })
    复制代码
    let resolvePromise = (promise2, x, resolve, reject) => {
        // 如果promise2和x相等,就抛出一个类型错误
        if (promise2 === x) {
            return reject(new TypeError('错了'));
        }
        // 只允许调用一次resolvePromise
        let called;
        // 如果x不是一个常量继续解析
        if (x !== null && (typeof x === 'function' || typeof x === 'object')) {
            // 调用x.then的时候可能会报错,因为我们有可能和别人的Promise库混用 
            try {
                let then = x.then;
                
                // 如果then是一个函数,证明x是一个promise,继续解析
                if (typeof then === 'function') {
                    then.call(x, y => {
                        if (called) {
                            return;
                        } else {
                            called = true;
                        }
                        resolvePromise(promise2, y, resolve, reject);
                    }, r => {
                        if (called) {
                            return;
                        } else {
                            called = true;
                        }
                        reject(r);
                    })
                } else {
                    // 说明x可能是一个普通对象,不是一个promise
                    resolve(x);
                }
            } catch (e) {
                if (called) {
                    return;
                } else {
                    called = true;
                }
                reject(e);
            }
        } else {
            // 说明x是一个常量,直接执行resolve
            resolve(x);
        }
    }
    复制代码

    接下来我们来实现Promise.resolve Promise.reject Promise.all

    class Promise {
        contructor {...}
        then() {...}
        
        // 其实Promise.reject和Promise.reject非常简单
        static resolve(value) {
            return new Promise((resolve) => {
                resolve(value);
            })
        }
        static reject(reason) {
            return new Promise((resolve, reject) => {
                reject(reason);
            })
        }
        
        // all方法
        static all(promises) {
            return new Promise((resolve, reject) => {
                // 用来保存结果
                let arr = [];
                let index = 0;
                
                const saveData = (i, data) => {
                    arr[i] = data;
                    if (++index === promises.length) {
                        resolve(arr);
                    }
                }
                
                for (let i = 0; i < promises.length; i++) {
                
                    promises[i].then(data => {
                        saveData(i, data);
                    }, reject)
                }
            })
        }
    }
    复制代码

    好了,接下来我们来一个完整版的promise

    class Promise {
        contructor() {
            this.status = 'pending';
            this.value;
            this.reason;
        
            // 用来保存成功时执行的回调函数
            this.onSuccessCallback = [];
            
            // 用来保存失败时执行的回调函数
            this.onErrorCallback = [];
        
            let resolve = val => {
                if (this.status === 'pending') {
                    this.value = val;
                    this.status = 'fulfilled';
                    
                    // 状态改变时 依次执行成功的回调
                    this.onSuccessCallback.forEach(fn => fn());
                }
            }
        
            let reject = reason => {
                if (this.status === 'pending') {
                    this.reason = reason;
                    this.status = 'rejected';
                    
                    // 状态改变时 依次执行失败的回调
                    this.onErrorCallback.forEach(fn => fn());
                }
            }
        
            // 执行executor可能会直接抛出异常,我们用try catch包起来
            try {
                executor(resolve, reject); 
            } catch (e) {
                // 如果抛出异常,我们直接将状态改为失败态
                reject(e);
            }
        }
        
        static resolve(value) {
            return new Promise((resolve) => {
                resolve(value);
            })
        }
        static reject(reason) {
            return new Promise((resolve, reject) => {
                reject(reason);
            })
        }
        
        static all(promises) {
            return new Promise((resolve, reject) => {
                // 用来保存结果
                let arr = [];
                let index = 0;
                
                const saveData = (i, data) => {
                    arr[i] = data;
                    if (++index === promises.length) {
                        resolve(arr);
                    }
                }
                
                for (let i = 0; i < promises.length; i++) {
                
                    promises[i].then(data => {
                        saveData(i, data);
                    }, reject)
                }
            })
        }
        
        then(onFulfilled, onRejected) {
            // 如果onFulfilled和onFulfilled 不是一个函数,我们给一个默认值
            onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : val => val;
            onRejected = typeof onRejected === 'function' ? onRejected : err => { throw err };
            // 我们将所有东西都包到一个promise实例中,最后返回这个实例,这样就可以实现链式调用
            let promise2;
            // `onFulfilled` 和 `onRejected`都是异步调用,我们先用一个定时器实现异步调用
            promise2 = new Promise((resolve, reject) => {
    
                if (this.status === 'fulfilled') {
                    setTimeout(() => {
                        try {
                            // 有一个返回值x,运行解析函数resolvePromise
                            let x = onFulfilled(this.value);
                            resolvePromise(promise2, x, resolve, reject)
                        } catch (e) {
                            reject(e);
                        }
                    }, 0);
                }
                    
                if (this.status === 'rejected') {
                    setTimeout(() => {
                        try {
                            // 有一个返回值x,运行解析函数resolvePromise
                            let x = onRejected(this.reason);;
                            resolvePromise(promise2, x, resolve, reject)
                        } catch (e) {
                            reject(e);
                        }
                    }, 0);
                }
            
                if (this.status === 'pending') {
        
                    // 将成功回调和失败回调都存起来,等待状态改变,再依次执行对应的方法
                    this.onSuccessCallback.push(() => {
                        setTimeout(() => {
                            try {
                                // 有一个返回值x,运行解析函数resolvePromise
                                let x = onFulfilled(this.value);
                                resolvePromise(promise2, x, resolve, reject)
                            } catch (e) {
                                reject(e);
                            }
                        }, 0);
                    });
                
                    this.onErrorCallback.push(() => {
                        setTimeout(() => {
                            try {
                                // 有一个返回值x,运行解析函数resolvePromise
                                let x = onRejected(this.reason);;
                                resolvePromise(promise2, x, resolve, reject)
                            } catch (e) {
                                reject(e);
                            }
                        }, 0);
                    });
                }
            });
            return promise2;
        }
    }
    
    let resolvePromise = (promise2, x, resolve, reject) => {
        // 如果promise2和x相等,就抛出一个类型错误
        if (promise2 === x) {
            return reject(new TypeError('错了'));
        }
        // 只允许调用一次resolvePromise
        let called;
        // 如果x不是一个常量继续解析
        if (x !== null && (typeof x === 'function' || typeof x === 'object')) {
            // 调用x.then的时候可能会报错,因为我们有可能和别人的Promise库混用 
            try {
                let then = x.then;
                
                // 如果then是一个函数,证明x是一个promise,继续解析
                if (typeof then === 'function') {
                    then.call(x, y => {
                        if (called) {
                            return;
                        } else {
                            called = true;
                        }
                        resolvePromise(promise2, y, resolve, reject);
                    }, r => {
                        if (called) {
                            return;
                        } else {
                            called = true;
                        }
                        reject(r);
                    })
                } else {
                    // 说明x可能是一个普通对象,不是一个promise
                    resolve(x);
                }
            } catch (e) {
                if (called) {
                    return;
                } else {
                    called = true;
                }
                reject(e);
            }
        } else {
            // 说明x是一个常量,直接执行resolve
            resolve(x);
        }
    }
    
    复制代码

猜你喜欢

转载自juejin.im/post/5b58300b6fb9a04fea58a27e