zoukankan      html  css  js  c++  java
  • JS/ES6 Promise 的不完全实现

    零、原文与资料

      1. 手写 Promise

      2. 最简实现Promise,支持异步链式调用(20行)

    一、Promise/A+ 规范

    1.Promise存在三个状态:pending(等待态)、fulfilled(成功态)、rejected(失败态);
    2.pending为初始态,并可以转化为fulfilled和rejected;
    3.成功时,不可转为其他状态,且必须有一个不可改变的值 (value);
    4.失败时,不可转为其他状态,且必须有一个不可改变的原因 (reason);
    5.new Promise(executor = (resolve, reject) => {resolve(value)}), resolve(value)将状态置为 fulfilled;
    6.new Promise(executor = (resolve, reject) => {reject(reason)}), reject(reason)将状态置为 rejected;
    7.如果 executor 执行异常也会 reject();
    8.thenable: then(onFulfilled, onRejected?);
      8.1 onFulfilled: status 为 fulfilled,执行 onFulfilled, 传入 value
      8.2 onRejected: status 为 rejected, 执行 onRejected

    二、同步 Promise

    同步 Promise 没啥需要特别注意的地方,代码如下:

    // 1.Promise存在三个状态:pending(等待态)、fulfilled(成功态)、rejected(失败态)
    const STATUS_PENDING = 'pending'
    const STATUS_FULFILLED = 'fulfilled'
    const STATUS_REJECTED = 'rejected'
    class myPromise {
        constructor(executor) {
            // pending为初始态,并可以转化为fulfilled和rejected
            this.status = STATUS_PENDING
            this.value = '' // 3
            this.reason = '' // 4
    
            let resolve = value => {
                // 5.
                if (this.status === STATUS_PENDING) {
                    this.status = STATUS_FULFILLED
                    this.value = value
                }
            }
            let reject = reason => {
                //6.
                if (this.status === STATUS_PENDING) {
                    this.status = STATUS_REJECTED
                    this.reason = reason
                }
            }
            // 7.
            try {
                executor(resolve, reject);
            } catch (err) {
                reject(err);
            }
        }
      // 8.
        then(onFulfilled = () => {}, onRejected = () => {}) {
            // 8.1
            if (this.status === STATUS_FULFILLED) {
                onFulfilled(this.value)
            }
            // 8.2
            if (this.status === STATUS_REJECTED) {
                onRejected(this.reason)
            }
        }
    }
    
    
    let ps = new myPromise(resolve => {
        console.log('before resolve')
        resolve(1)
    })
    ps.then(res => {
        console.log(res)
    })
    
    let pe = new myPromise((resolve, reject) => {
        console.log('before reject')
        reject('reject error')
    })
    pe.then(res => {
        console.log(res)
    }, error => {
        console.log(error)
    })
    

    在上面的两个例子中,所有任务的执行都是同步的,可以与接下来的异步 promise 的执行顺序对比下。

    三、异步 Promise

    先放下我们的运行实例:

    let pa = new myPromise(resolve => {
        console.log('before resolve')
        setTimeout(()=>{
            resolve(1)
        },1000)
    })
    pa.then(res => {
        console.log(res)
    })
    

     在这里,我们的 executor 函数体中有异步的代码块,那么在执行 executor(resolve, reject); 的时候,setTimeout中的任务就会被推入异步执行栈中,等待主线程中的宏任务(详见 EventLoop in Js)全部计算完成再执行这个任务,因此,我们打断点会发现,  then() 的执行会早于  resolve(1) , 而在 then() 执行的时候,pa.status 依然是 pending,接下啦根据逻辑判断 then 函数执行完成退出,然后执行异步任务,整个代码执行完毕,故控制台只会打印出 'before resolve'。
    明白了这里的执行顺序,我们即可以进行完善,代码如下:(标记 改 A)

    const STATUE_PENDING = 'pending';
    const STATUE_FULFILLED = 'fulfilled';
    const STATUS_REJECTED = 'rejected';
    
    class MyPromise {
        constructor(executor) {
            // pending 是初始态,并可以转化成 fulfilled 和 rejected
            this.status = STATUE_PENDING;
            this.value = ''; // 3.
            this.reason = ''; // 4.
    
            // 改 A start
            // 存放成功的数组
            this.onResolvedCallbacks = [];
            // 存放失败的数组
            this.onRejectedCallbacks = [];
            // 改 A end
    
            let resolve = value => {
                // 5.
                if (this.status === STATUE_PENDING) {
                    this.status = STATUE_FULFILLED;
                    this.value = value;
    
                    // 改 A start
                    // 成功之后的执行栈, 
                    this.onResolvedCallbacks.forEach(fn => fn());
                    // 改 A end
                }
            }
            
            let reject = reason =>  {
                // 6.
                if (this.status === STATUE_PENDING) {
                    this.status = STATUS_REJECTED;
                    this.reason = reason;
    
                    // 改 A start
                    // 成功之后的执行栈, 
                    this.onRejectedCallbacks.forEach(fn => fn());
                    // 改 A end
                }
            }
    
    
            // 7. 
            try {
                executor(resolve, reject);
            } catch (error) {
                reject(err);
            }
        }
    
        // 8
        then(onFulfilled = () => {}, onRejected = () => {}){
            // 8.1
            if (this.status === STATUE_FULFILLED) onFulfilled(this.value);
            // 8.2
            if (this.status === STATUS_REJECTED) onRejected(this.reason);
    
            // 改 A start
            // 如果是处于异步任务的
            if (this.status === STATUE_PENDING) {
                // 推入相应的执行栈
                this.onResolvedCallbacks.push(() => onFulfilled(this.value));
                this.onRejectedCallbacks.push(() => onRejected(this.reason));
            }
            // 改 A end
        }
    }
    

    为什么要分析下这边的执行顺序,一来复习下 EventLoop,二来对下面的链式调用的理解比较重要(这里也是打断点才发现的,推翻了一直以来对 Promise.then 的理解,之前一直认为 executor 中的异步任务执行完了才真正的去执行 then 函数和里面的onFulfilled/onRejected 函数)。

    四、new Promise().then().then()... 链式调用

    Promise 的链式调用和 jquery 的链式调用是不同的,在 jquery 或者一些其他的三方包中,我们在函数末尾加上 return this  即可实现,所以这里无论链多少,都是在同一个对象上做文章。而 Promise 的每一次(链式)调用,其都会产生一个新的 Promise 对象, 并基于这个新的 Promise 调用 then 函数,虽然我们写的时候是.then().then()...。 首先我们来看实例:

    let pc = new MyPromise((resolve, reject) => {
        console.log(0);
        setTimeout(() => {
            resolve(1);
        }, 3000);
    })
    pc.then(res => {
        console.log(res);
    
        return new MyPromise(resolve => {
            console.log(2);
            setTimeout(() => {
                resolve(3)
            }, 3000);
        })
    }).then(res => {
        console.log(res);
    })
    

    注意下结构,我们在第一个 then 的参数函数中会有一个新的 Promise 返回。
    然后是 MyPromise 类:

    const STATUE_PENDING = 'pending';
    const STATUE_FULFILLED = 'fulfilled';
    const STATUS_REJECTED = 'rejected';
    
    function resolvePromise(promise2, x, resolve, reject) {
        // 处理循环引用报错
        if (x === promise2) {
            // reject 报错
            return reject(new TypeError('chaining cycle detected for promise'));
        }
    
        // 记录, 防止多次调用
        let called;
    
        // x 是对象(不包括 null)或者函数
        if (x != null && (typeof x === 'object' || typeof x === 'function')) {
            try {
                // A+ 规定,声明 then = x 的 then 方法
                let then = x.then;
                // then 是 function,则默认是 promise
                if (typeof then === 'function') {
                    // 就让 then 执行, 第一个参数是 this, 后面是成功的回调和失败的回调
    
                    then.call(x, y => {
                        // 成功和失败只能调用一个
                        if (called) return;
                        called = true;
    
                        // resolve 的结果依旧是promise 那就继续解析
                        resolvePromise(promise2, y, resolve, reject);
                    }, err => {
                        // 成功和失败只能调用一个
                        if (called) return;
                        called = true;
    
                        // 失败,停止继续调用
                        reject(err);
                    })
                } else {// 不是的话直接 resolve 即可
                    resolve(x);
                }
    
            } catch (error) {
                // 出错,即失败
                if (called) return;
                called = true;
    
                // 取 then 出错了那就不继续了
                reject(error);
            }
        } else {
            resolve(x);
        }
    }
    
    class MyPromise {
        constructor(executor) {
            this.status = STATUE_PENDING;
            this.value = '';
            this.reason = '';
    
    
            this.onResolvedCallbacks = [];
            this.onRejectedCallbacks = [];
    
            let resolve = value => {
                if (this.status === STATUE_PENDING) {
                    this.status = STATUE_FULFILLED;
                    this.value = value;
    
                    this.onResolvedCallbacks.forEach(fn => fn());
                }
            }
            
            let reject = reason =>  {
                if (this.status === STATUE_PENDING) {
                    this.status = STATUS_REJECTED;
                    this.reason = reason;
    
                    this.onRejectedCallbacks.forEach(fn => fn());
                }
            }
    
            try {
                executor(resolve, reject);
            } catch (error) {
                reject(err);
            }
        }
    
        then(onFulfilled, onRejected){
            // onFulfilled 不是函数, 则忽略,直接返回 value
            onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : value => value;
            // onRejected 不是函数, 则忽略,直接扔出错误
            onRejected = typeof onRejected === 'function' ? onRejected : err => {
                throw err;
            };
    
            let promise2 = new MyPromise((resolve, reject) => {
                if (this.status === STATUE_FULFILLED) {
                    // 推一个异步任务
                    setTimeout(() => {
                        try {
                            let x = onFulfilled(this.value);
                            resolvePromise(promise2, x, resolve, reject);    
                        } catch (error) {
                            reject(error)
                        }
                    }, 0);
                }
    
                if (this.status === STATUS_REJECTED) {
                    // 推一个异步任务
                    setTimeout(() => {
                        try {
                            let x = onRejected(this.reason);
                            resolvePromise(promise2, x, resolve, reject);    
                        } catch (error) {
                            reject(error)
                        }
                    }, 0);
                }
    
                if (this.status === STATUE_PENDING) {
                    // 推到执行栈中
                    this.onResolvedCallbacks.push(() => {
                        setTimeout(() => {
                            try {
                                let x = onFulfilled(this.value);
                                resolvePromise(promise2, x, resolve, reject);    
                            } catch (error) {
                                reject(error)
                            }
                        }, 0);
                    });
    
                    this.onRejectedCallbacks.push(() => {
                        setTimeout(() => {
                            try {
                                let x = onRejected(this.reason);
                                resolvePromise(promise2, x, resolve, reject);    
                            } catch (error) {
                                reject(error)
                            }
                        }, 0); 
                    })
                }
    
            });
    
            return promise2;
        }
    }
    

    这里要注意的是 then 函数的执行会产生一个新的 Promise, 第一个 then 函数的参数函数的执行也会产生一个新的 Promise。

    五、其他:catch、resolve、reject、race和all

    这里除 catch 外其余均是静态方法:

     1. catch(特殊的 then 方法):

    catch(fn){
        return this.then(null,fn)
    }
    

     2.reslove(resolve 一个值)

    MyPromise.resolve = val => new Promise(resolve=> resolve(val))
    

     3.reject(reject 一个值)

    MyPromise.reject = val => new Promise((resolve,reject)=> reject(val))
    

     4.race Promise.race([p1, p2, p3])里面哪个结果获得的快,就返回那个结果,不管结果本身是成功状态还是失败状态。

    MyPromise.race = promises => {
        return new MyPromise((resolve, reject) =>
            promises.forEach(pro => pro.then(resolve, reject))
        )
    }
    

     5.all Promise.all可以将多个Promise实例包装成一个新的Promise实例。同时,成功和失败的返回值是不同的,成功的时候返回的是一个结果数组,而失败的时候则返回最先被reject失败状态的值。

    MyPromise.all = function (promises) {
        return new Promise((resolve, reject) => {
            let index = 0;
            let result = [];
            if (promises.length === 0) {
                resolve(result);
            } else {
                function processValue(i, data) {
                    result[i] = data;
                    if (++index === promises.length) {
                        resolve(result);
                    }
                }
                for (let i = 0; i < promises.length; i++) {
                    //promises[i] 可能是普通值
                    Promise.resolve(promises[i]).then((data) => {
                        processValue(i, data);
                    }, (err) => {
                        reject(err);
                        return;
                    });
                }
            }
        });
    }
    

    六、一点感悟

    Promise/A+ 规范还是值得每一个合格的前端开发去阅读的。

        得:再一次复习了 eventLoop,推翻了之前对 then 函数的执行时机的错误认识;

        目标:完全理清楚链式调用的任务栈和执行顺序以及 Promise.all 的原理;

  • 相关阅读:
    自主问题--KMP算法
    题解--luogu--CSP2019.S.Day2.T4--Emiya 家今天的饭
    浅谈——RMQ
    浅谈——LCA
    NOIP(si le)或者CSP初赛之——前序中序后序,前缀中缀后缀
    浅说——查分约束
    浅说——tarjan
    C++ RQNOJ 星门龙跃
    C++ 洛谷 1261:【例9.5】城市交通路网
    刷题
  • 原文地址:https://www.cnblogs.com/cc-freiheit/p/12659992.html
Copyright © 2011-2022 走看看