手撕Promise A+

283 阅读4分钟

Promise A+规范

下载地址PromiseA+

Promise是ES6中的内置类,用来管理异步编程,但是其兼容性很差。

image.png

从A+规范角度来讲,Promise有以下特点。

1.Promise必须是个object或者function
2.必须具备then方法


搭建Promise主体

new Promise的时候,如果不传递参数,浏览器会抛出一个异常,所以创建的Promise构造函数必须传递参数。且Promise也必须可以被new执行。

  /*自定义Promise*/
    function Promise(executor) {
        if (!(self instanceof Promise)) throw new TypeError('undefined is not a promise!');
        if (typeof executor !== "function") throw new TypeError('Promise resolver ' + executor + ' is not a function!');
    }

new Promise的时候会立即执行executor函数。并且会传递resolvereject两个方法。
形成了一个相互回调函数处理模式,把一个函数A作为值,传递到另外一个执行的函数B中,在B中可以把A执行。

(function(){
    /*自定义Promise*/
    function Promise(executor) {
        if (!(self instanceof Promise)) throw new TypeError('undefined is not a promise!');
        if (typeof executor !== "function") throw new TypeError('Promise resolver ' + executor + ' is not a function!');

        executor(function resolve(result) {

        }, function reject(reason) {

        });
    }
})();

let p1 = new Promise(function (resolve, reject) {
    resolve('OK');
    // reject('NO');
});

在Promise中根绝状态来辨别是成功还是失败,state为当前Promise的状态;

pending等待
fulfilled成功
rejected失败

(function(){
    /*自定义Promise*/
    function Promise(executor) {
        var self = this,
            change;
        if (!(self instanceof Promise)) throw new TypeError('undefined is not a promise!');
        if (typeof executor !== "function") throw new TypeError('Promise resolver ' + executor + ' is not a function!');

        // 实例具备的属性
        self.state = 'pending';
        self.result = undefined;

        // 改变状态的方法
        change = function change(state, result) {
            if (self.state !== 'pending') return;
            self.state = state;
            self.result = result;
        };
        
        //异常捕获
        try {
            executor(function resolve(result) {
                change('fulfilled', result);
            }, function reject(reason) {
                change('rejected', reason);
            });
        } catch (err) {
            change('rejected', err);
        }
    }
})();

Promise实例方法

Promise.prototype = {
        constructor: Promise,
        self: true,//判断是否为自己写的Promise
        then: function (onFulfilled, onRejected) {},
        catch: function () {}
    };

then方法的实现

Promise的核心是then的实现,在执行then方法的时候有以下几种情况。

情况1:如果已经知道对应实例的状态是成功还是失败的,则创建一个“异步微任务”,后期执行onFulfilled/onRejected
情况2:如果此时还不知道实例状态,就先把onFulfilled/onRejected存储起来,后期更改其状态,再通知方法执行即可“异步微任务”

基于queueMicrotask可以创建异步微任务,但是兼容性很差,所以这一版本用settimeout模拟异步微任务。

then: function (onFulfilled, onRejected) {
           var self = this;
             switch (self.state) {
                   case 'fulfilled':
                       setTimeout(function () {
                           onFulfilled(self.result);
                       }, 0);
                       break;
                   case 'rejected':
                       setTimeout(function () {
                           onRejected(self.result);
                       }, 0);
                       break;
                   default:
                       self.onFulfilledCallbacks.push(onFulfilled);
                       self.onRejectedCallbacks.push(onRejected);
               }
       }

相应的,Promise的change方法需要根据传递的数组进行处理。

   // 实例具备的属性
        self.state = 'pending';
        self.result = undefined;
        self.onFulfilledCallbacks = [];
        self.onRejectedCallbacks = [];

        // 改变状态的方法
        change = function change(state, result) {
            if (self.state !== 'pending') return;
            self.state = state;
            self.result = result;
            var callbacks = state === 'fulfilled' ? self.onFulfilledCallbacks : self.onRejectedCallbacks,
                i = 0,
                len = callbacks.length,
                callback;
            if (callbacks.length > 0) {
                setTimeout(function () {
                    for (; i < len; i++) {
                        callback = callbacks[i];
                        if (typeof callback === "function") callback(result);
                    }
                }, 0);
            }
        };

至此,then的方法就实现了,但是现在的代码并不支持then链的调用,也就是说实例在调用then 后不能继续调用then。

每一次执行then方法,都会返回一个全新的Promise实例
状态:是由上一个then传入的onFulfilled/onRejected执行决定的

  • 方法执行看返回值,如果返回的不是promise实例,则方法执行不报错,新实例状态就是fulfilled,并且return的结果是新实例的结果,反之执行报错则认为新实例是失败的,结果是报错原因
  • 如果返回的也是一个promise实例,这样的话状态和结果直接影响了实例的状态和结果
// 验证是否为Promise实例「THENABLE」
    function isPromise(x) {
        if (x == null) return false;
        if (/^(object|function)$/i.test(typeof x)) {
            if (typeof x.then === "function") {
                return true;
            }
        }
        return false;
    }

    // 处理onFulfilled或者onRejected方法执行返回结果的处理
    function handle(promiseNew, x, resolve, reject) {
        if (x === promiseNew) throw new TypeError('Chaining cycle detected for promise #<Promise>');
        if (isPromise(x)) {
            try {
                x.then(resolve, reject);
            } catch (err) {
                reject(err);
            }
            return;
        }
        // 返回值不是promise实例,而且执行没有报错,则promiseNew一定是成功的,x是它的结果
        resolve(x);
    }
    
    
    then: function (onFulfilled, onRejected) {
            var self = this,
                promiseNew,
                x;

            promiseNew = new Promise(function (resolve, reject) {
                // resolve & reject 可以设置返回的@TP「新promise实例」是成功还是失败以及结果等
                // 但是执行哪个方法,由监听onFulfilled|onRejected方法报错以及返回值来决定
                switch (self.state) {
                    case 'fulfilled':
                        setTimeout(function () {
                            try {
                                x = onFulfilled(self.result);
                                handle(promiseNew, x, resolve, reject);
                            } catch (err) {
                                reject(err);
                            }
                        }, 0);
                        break;
                    case 'rejected':
                        setTimeout(function () {
                            try {
                                x = onRejected(self.result);
                                handle(promiseNew, x, resolve, reject);
                            } catch (err) {
                                reject(err);
                            }
                        }, 0);
                        break;
                    default:
                        self.onFulfilledCallbacks.push(function (result) {
                            try {
                                x = onFulfilled(result);
                                handle(promiseNew, x, resolve, reject);
                            } catch (err) {
                                reject(err);
                            }
                        });
                        self.onRejectedCallbacks.push(function (reason) {
                            try {
                                x = onRejected(reason);
                                handle(promiseNew, x, resolve, reject);
                            } catch (err) {
                                reject(err);
                            }
                        });
                }
            });
            return promiseNew;
        },

在执行的过程中需要捕获异常,一旦抛出异常,直接返回reject


在使用Promise的时候,如果then中的某个方法不设置,其默认具备“穿透性”,【顺延到下一个then中的同状态的方法】

if (typeof onFulfilled !== "function") {
      onFulfilled = function onFulfilled(result) {
            return result;
      };
}

if (typeof onRejected !== "function") {
      onRejected = function onRejected(reason) {
            throw reason;
      };
}

catch的实现

catch: function (onRejected) {
   return this.then(null, onRejected);
}

Promise私有静态方法

// 普通对象:私有静态方法
    Promise.resolve = function resolve() {};
    Promise.reject = function reject() {};
    Promise.all = function all() {};

Promise.resolve的实现

Promise.resolve返回一个状态成功的promise实例

Promise.resolve = function resolve(result) {
        return new Promise(function (resolve) {
            resolve(result);
        });
    };

Promise.reject的实现

Promise.reject返回一个状态失败的promise实例

Promise.reject = function reject(reason) {
        return new Promise(function (_, reject) {
            reject(reason);
        });
    };

Promise.all的实现

Promise.all返回一个全新的的promise实例,等待集合中所有的promise实例都成功,才算成功,结果是按照顺序依次存储集合中的每一项的结果。

  • 如果某一项不是promise的实例,我们把它作为一个成功的promise实例即可。
  • 如果集合中有一项是失败的,则实例就是失败的。
    Promise.all = function all(promises) {
        var promiseNew,
            results = [],
            n = 0;
        if (!Array.isArray(promises)) throw new TypeError(promises + ' is not iterable');

        // 控制集合中的每一项都是promise实例
        promises = promises.map(function (promise) {
            if (!isPromise(promise)) return Promise.resolve(promise);
            return promise;
        });

        // 返回一个全新的promise实例
        promiseNew = new Promise(function (resolve, reject) {
            promises.forEach(function (promise, index) {
                promise.then(function (result) {
                    // result存储的是当前迭代这一项的成功结果
                    n++;
                    results[index] = result;

                    // 都处理成功
                    if (n >= promises.length) resolve(results);
                }).catch(function (reason) {
                    // 只要有一项失败,@AP就是失败
                    reject(reason);
                });
            });
        });
        return promiseNew;
    };

以上就是实现兼容IE10的PromiseA+。