实现Promise的catch、finally、all、race方法

2,315 阅读4分钟

上一篇文章根据Promise A+规范实现Promise,但是规范的Promise和ES6的Promise相比,还缺少部分功能,我们来依次实现他们。

Prmoise A+规范的Promise

const PENDING = 'pending';
const RESOLVED = 'resolved';
const REJECTED = 'rejected';
function resolvePromise(promise2, x, resolve, reject) {
    if (promise2 === x) {
        reject(new TypeError('Chaining cycle detected for promise #<Promise>'));
    }
    let called;
    if ((typeof x === 'object' && x != null) || typeof x === 'function') {
        try {
            const then = x.then;
            if (typeof then === 'function') {
                then.call(
                    x,
                    y => {
                        if (called) return;
                        called = true;
                        resolvePromise(promise2, y, resolve, reject);
                    },
                    e => {
                        if (called) return;
                        called = true;
                        reject(e);
                    }
                );
            } else {
                resolve(x);
            }
        } catch (e) {
            if (called) return;
            called = true;
            reject(e);
        }
    } else {
        resolve(x);
    }
}
class Promise {
    constructor(executor) {
    	this.status = PENDING; 
    	this.value = undefined;
    	this.reason = undefined;
    	this.onResolvedCallbacks = [];
    	this.onRejectedCallbacks = [];
    	const resolve = value => {
    	    if (value instanceof Promise) {
    	        // 如果value是个Promise,递归解析,直到value为普通值
    	        value.then(resolve, reject);
    	    }
            if (this.status === PENDING) {
            	this.status = RESOLVED;
            	this.value = value;
            	this.onResolvedCallbacks.forEach(fn => fn());
            }
    	};
    	const reject = reason => {
            if (this.status === PENDING) {
                this.status = REJECTED;
                this.reason = reason;
                this.onRejectedCallbacks.forEach(fn => fn());
            }
    	};
    	try {
    		executor(resolve, reject);
    	} catch (err) {
    		reject(err);
    	}
    }
    then(onFulfilled, onRejected) {
        onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : value => value;
        onRejected = typeof onRejected === 'function' ? onRejected : err => throw err;

        const promise2 = new Promise((resolve, reject) => {
            if (this.status === RESOLVED) {
                setTimeout(() => {
                    try {
                        const x = onFulfilled(this.value);
                        resolvePromise(promise2, x, resolve, reject);
                    } catch (e) {
                        reject(e);
                    }
                }, 0);
            }
            if (this.status === REJECTED) {
                try {
                    const x = onRejected(this.reason);
                    resolvePromise(promise2, x, resolve, reject);
                } catch (e) {
                    reject(e);
                }
            }
            if (this.status === PENDING) {
                this.onResolvedCallbacks.push(() => {
                    try {
                        const x = onFulfilled(this.value);
                        resolvePromise(promise2, x, resolve, reject);
                    } catch (e) {
                        reject(e);
                    }
                });
                this.onRejectedCallbacks.push(() => {
                    try {
                        const x = onRejected(this.reason);
                        resolvePromise(promise2, x, resolve, reject);
                    } catch (e) {
                        reject(e);
                    }
                });
            }
        });
        return promise2;
    }
}

// promise的延迟对象
Promise.defer = Promise.deferred = function () {
    let dfd = {};
    dfd.promise = new Promise((resolve,reject)=>{
        dfd.resolve = resolve;
        dfd.reject = reject;
    })
    return dfd
}

module.export = Promise;

catch方法

catch相当于then方法的变种

catch(errCallback) {
    return this.then(null, errCallback);
}

Promise静态方法

  • Promise.resolve(),快速创建一个成功的Promise;
  • Promise.reject(),快速创建一个失败的Promise。
class Promise {
    static resolve(value) {
        return new Promise(resolve=> {
            resolve(value);
        })
    }
    static reject(reason) {
        return new Promise(reject=> {
            reject(reason);
        })
    }
}

finally()方法

  • finally方法用于指定不管Promise对象最后状态如何都会执行的操作。它只接受一个普通的回调函数作为参数,该函数不管怎样都必须执行。它并不是表示最终的意思,也不会影响其他逻辑。
Promise.resolve('100').finally(
    ()=>{
         console.log('finally');
    }
).then(
    res=> {
        console.log(res);
    }
)
// 或
Promise.reject('200')
    .finally(
        ()=>{
             console.log('finally');
        }
    ).catch(
        err=> {
            console.log(err);
        }
    )
  • 但是如果返回的是一个Promise,要等待finally中的Promise执行完以后,才会走下一个,如果这个Promise是失败态,那么会把finally中reject的结果传给失败的回调。
// 成功态
Promise.resolve('100')
    .finally(
        ()=> {
            return new Promise(resolve=> {
                setTimeout(()=>{
                    resolve('success')
                }, 3000)
            })
        }
    )
    .then(
        res=> {
            console.log(res); // 等待3秒后打印 100;
        }
    )
// 失败态
Promise.resolve('100')
    .finally(
        ()=> {
            return new Promise((resolve,reject)=> {
                setTimeout(()=> {
                    reject('failed')
                }, 3000)
            })
        }
    )
    .catch(
        err=> {
            console.log(err); // 等待3秒后打印 failed;
        }
    )

知道了finally的用法,那我们就来实现它

class Promise {
    finally(callback) {
        return this.then(
            // finally前面是成功,就执行成功的回调,并把前面的参数向下传递
            value=> {
                // callback()执行完可能返回一个Promise
                // 也可能不返回一个Promise,为了兼容,嵌套一层Promise.resolve保证向下传递
                return Promise.resolve(callback()).then(()=> value)
            }
            // finally前面是失败,就执行失败的回调,并把前面的参数向下传递
            reason=> {
                return Promise.resolve(callback()).then(()=> throw reason)
            }
        )
    }
}

Promise.all()

如果有三个请求同时执行,其中一个请求失败,另外两个请求依旧会执行,promise不会中断,只是最终不会采纳另外两个请求的结果。

const isPromise = value => typeof value.then === 'function';
Promise.all = function(promises) { // 全部成功才成功
    return new Promise((resolve, reject) => {
        // 异步 :并发 (使用for循环迭代执行) 和 串行(借助回调 第一个完成后调用第二个)
        // 遍历数组 依次拿到执行结果
        let arr = [];
        let index = 0;
        const processData = (key, data) => {
            arr[key] = data; // 不能使用数组的长度来计算
            if(++index === promises.length){
                resolve(arr);
            }
        }
        for (let i = 0; i < promises.length; i++) {
            let result = promises[i];
            if (isPromise(result)) {
                result.then(
                    data => {
                        processData(i, data)
                    }, 
                    reject
                )
            } else {
                processData(i, result)
            }
        }
    });
}

Promise.race()

谁跑的快,用谁的,但是其他的也会执行,只是只采用最快的那个。

const isPromise = value => typeof value.then === 'function';
Promise.race = function(promises) { 
    return new Promise((resolve, reject) => {
        for (let i = 0; i < promises.length; i++) {
            let result = promises[i];
            if(isPromise(result)){
                result.then(resolve,reject)
            }else{
                resolve(result);
            }
        }
    });
}

使用Promise.race()“中断”Promise

使用快的Promise,“中断”慢的Promise,其实也不是中断,只是不采纳慢的Promise的结果

比如有个请求要10s返回,结果,我等不及了,2秒后我就要“中断”他

const promise1 = new Promise((resolve, reject)=> {
    setTimeout(()=> {
        resolve('OK 成功了');
    }, 10000)
})

// 把上面的promise1传入到wrap函数的race中
const wrap = promise=> {
    let abort;
    
    const p1 = new Promise((resolve, reject)=> {
        abort = reject;
    })
    
    // 思路:提前reject p1,那么race采纳的就是p1的结果
    const p2 = Promise.race([promise, p1]);
    p2.abort = abort;
    
    return p2;
}

const wrapPromise = wrap(promise1);

wrapPromise.then(
    data=> {
        console.log('success', data)
    },
    err=> {
        console.log('failed', err); // failed 超时!
    }
)
console.log(wrapPromise);

setTimeout(()=> {
    wrapPromise.abort('超时!');
}, 2000)