手撕PromiseA+

148 阅读1分钟

Promise实现

// 创建异步微任务  queueMicroTask(function(){})
(function () {
    /* 工具类方法 */
    var isPromise = function isPromise(obj) {
        if ((obj !== null && typeof obj === "object") || (typeof obj === "function")) {
            if (typeof obj.then === "function") {
                return true;
            }
        }
        return false;
    };
    var resolvePromise = function resolvePromise(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;
        }
        resolve(x);
    };
    /* 构造函数 */
    function Promise(executor) {
        if (typeof executor !== 'function') throw new TypeError('Promise resolver ' + executor + ' is not a function');
        var self = this;
        self.state = 'pending';
        self.value = undefined;
        self.onFulfilledCallbacks = [];
        self.onRejectedCallbacks = [];
        var change = function change(state, value) {
            if (self.state !== 'pending') return;
            self.state = state;
            self.value = value;
            setTimeout(function () {
                var callbacks = self.state === 'fulfilled' ? self.onFulfilledCallbacks : self.onRejectedCallbacks;
                for (var i = 0; i < callbacks.length; i++) {
                    var item = callbacks[i];
                    if (typeof item === "function") {
                        item(self.value);
                    }
                }
            });
        };
        try {
            executor(function resolve(result) {
                change('fulfilled', result);
            }, function reject(reason) {
                change('rejected', reason);
            });
        } catch (err) {
            change('rejected', err);
        }
    }

    /* 原型 */
    Promise.prototype = {
        constructor: Promise,
        customize: true,
        then: function (onfulfilled, onrejected) {
            if (typeof onfulfilled !== "function") {
                onfulfilled = function onfulfilled(value) {
                    return value;
                };
            }
            if (typeof onrejected !== "function") {
                onrejected = function onrejected(value) {
                    throw value;
                };
            }
            var self = this,
                promiseNew = null;
            promiseNew = new Promise(function (resolve, reject) {
                switch (self.state) {
                    case 'fulfilled':
                        setTimeout(function () {
                            try {
                                var x = onfulfilled(self.value);
                                resolvePromise(promiseNew, x, resolve, reject);
                            } catch (err) {
                                reject(err);
                            }
                        });
                        break;
                    case 'rejected':
                        setTimeout(function () {
                            try {
                                var x = onrejected(self.value);
                                resolvePromise(promiseNew, x, resolve, reject);
                            } catch (err) {
                                reject(err);
                            }
                        });
                        break;
                    default:
                        self.onFulfilledCallbacks.push(function (value) {
                            try {
                                var x = onfulfilled(value);
                                resolvePromise(promiseNew, x, resolve, reject);
                            } catch (err) {
                                reject(err);
                            }
                        });
                        self.onRejectedCallbacks.push(function (value) {
                            try {
                                var x = onrejected(value);
                                resolvePromise(promiseNew, x, resolve, reject);
                            } catch (err) {
                                reject(err);
                            }
                        });
                }
            });
            return promiseNew;
        },
        catch: function (onrejected) {
            var self = this;
            return self.then(null, onrejected);
        }
    };

    /* 对象 */
    Promise.resolve = function resolve(value) {
        return new Promise(function (resolve) {
            resolve(value);
        });
    };
    Promise.reject = function reject(value) {
        return new Promise(function (_, reject) {
            reject(value);
        });
    };
    Promise.all = function all(promises) {
        return new Promise(function (resolve, reject) {
            try {
                var index = 0,
                    len = promises.length,
                    results = [];
                for (var i = 0; i < len; i++) {
                    (function (i) {
                        var item = promises[i];
                        if (!isPromise(item)) {
                            index++;
                            results[i] = item;
                            index === len ? resolve(results) : null;
                            return;
                        }
                        item.then(function (result) {
                            index++;
                            results[i] = result;
                            index === len ? resolve(results) : null;
                        }, function (reason) {
                            reject(reason);
                        });
                    })(i);
                }
            } catch (err) {
                reject(err);
            }
        });
    };

    /* 暴露API */
    if (typeof module === 'object' && typeof module.exports === 'object') {
        module.exports = Promise;
    }
    if (typeof window !== 'undefined') {
        window.Promise = Promise;
    }
})();

简单测试

var p1 = new Promise((resolve, reject) => {
    resolve(100)
})
let p2 = p1.then(function (value) {
    console.log('成功', value)
    return new Promise((resolve, reject) => { reject(-100) })
}, function (reason) {
    console.log('失败', reason)
})
let p3 = p2.then(function (value) {
    console.log('成功', value)
}, function (reason) {
    console.log('失败', reason)
    return 200
})
let p4 = p3.then(function (value) {
    console.log('成功', value)
}, function (reason) {
    console.log('失败', reason)
})
console.log('end')

包测试

1. 安装

npm install promises-aplus-tests --save-dev

2. 手写代码加入

Promise.deferred = function(){
    var result = {}
    result.promise = new Promise(function(resolve,reject){
        result.resolve = resolve;
        result.reject = reject;
    })
    return result;
}

3. 配置命令

{
    "scripts":{
        "test":"promises-aplus-tests myPromise.js"
    }
}

4. 开始测试

npm run test