手写Promise,经过Promise/A+的872个测试

githubjavascript

博客java

Promise的声明

当咱们使用Promise的时候,一般都是new Promise((resolve, reject) => {})git

所以咱们能够看出:github

  • Promise是一个类;
  • Promise类的构造函数的第一个参数是函数,这个函数叫处理器函数(executor function);
  • 而在处理器函数中,有了两个参数:resolvereject
    • 当异步任务顺利完成且返回结果值的时候,咱们会调用resolve函数;
    • 当异步任务失败且返回失败缘由(一般是一个错误对象)时,会调用reject函数。

所以,咱们能够初步声明一下Promise类。shell

class Promise {
    /** * 构造器 * @returns {Promise<object>} * @param executor<function>: executor有两个参数:resolve和reject */
    constructor(executor) {
        // resolve 成功
        const resolve = () => {};

        // reject 失败
        const reject = () => {};

        // 执行 executor
        executor(resolve,reject);
    }
}
复制代码

实现Promise的基本状态

Promise存在着三种状态:pending(等待态)、fulfilled(成功态)和rejected(失败态):数组

  • Promise的初始状态是pending状态;
  • pending状态能够转换为fulfilled状态和rejected状态;
  • fulfilled状态不能够转为其余状态,且必须有一个不可改变的值(value);
  • rejected状态不能够转为其余状态,且必须有一个不可改变的缘由(reason);
  • 当在处理器函数中调用resolve函数并传入参数value,则状态改变为fulfilled,且不能够改变;
  • 当在处理器函数中调用reject函数并传入参数reason,则状态改变为rejected,且不能够改变;
  • 若处理器函数执行中报错,直接执行reject函数。

所以,咱们须要在Promise类中设置三个变量:state(状态变量),value(成功值的变量)和reason(失败缘由的变量),而后在resolve函数、reject函数以及执行executor函数报错的时候改变state的值。promise

class Promise {
    constructor(executor) {
        // 初始化状态
        this.state = 'pending';
        // 成功的值
        this.value = undefined;
        // 失败的缘由
        this.reason = undefined;
        
        /** * resolve 成功函数 * @param value<any>: 成功的值 */
        const resolve = (value) => {
            // 只能在状态为pending的时候执行
            if(this.state === 'pending'){
                // resolve调用后,state转化为fulfilled
                this.state = 'fulfilled';
                // 存储value
                this.value = value;
            }
        };

        /** * reject 失败函数 * @param reason<any>: 失败的缘由 */
        const reject = (reason) => {
            // 只能在状态为pending的时候执行
            if(this.state === 'pending'){
                // resolve调用后,state转化为rejected
                this.state = 'rejected';
                // 存储reason
                this.reason = reason;
            }
        };

        // 若是executor执行报错,直接执行reject()
        try {
            executor(resolve,reject);
        }catch (e){
            reject(e);
        }
    }
}
复制代码

then方法

Promise有一个then方法,而该方法中有两个参数:onFulfilledonRejectedmarkdown

  • 这两个参数都是一个函数,且会返回一个结果值;
  • 当状态为fulfilled,只执行onFulfilled,传入this.value
  • 当状态为rejected,只执行onRejected,传入this.reason

所以咱们能够来实现一下then方法。异步

class Promise {
   constructor(executor) {...}

    /** * then 方法 * @param onFulfilled<function>: 状态为fulfilled时调用 * @param onRejected<function>: 状态为rejected时调用 */
    then(onFulfilled, onRejected) {
        // 状态为fulfilled的时候,执行onFulfilled,并传入this.value
        if(this.state === 'fulfilled'){
            /** * onFulfilled 方法 * @param value<function>: 成功的结果 */
            onFulfilled(this.value)
        }

        // 状态为rejected的时候,onRejected,并传入this.reason
        if(this.state === 'rejected'){
            /** * onRejected 方法 * @param reason<function>: 失败的缘由 */
            onRejected(this.reason)
        }
    }
}
复制代码

异步实现

Promise实际上一个异步操做:async

  • resolve()是在setTimeout内执行的;
  • 当执行then()函数时,若是状态是pending时,咱们须要等待状态结束后,才继续执行,所以此时咱们须要将then()的两个参数onFulfilledonRejected存起来;
  • 由于一个Promise实例能够调用屡次then(),所以咱们须要将onFulfilledonRejected各类用数组存起来。

所以咱们能够借着完善代码:

class Promise {
    /** * 构造器 * @returns {Promise<object>} * @param executor<function>: executor有两个参数:resolve和reject */
    constructor(executor) {
        this.state = 'pending';
        this.value = undefined;
        this.reason = undefined;
        // 存储onFulfilled的数组
        this.onResolvedCallbacks = [];
        // 存储onRejected的数组
        this.onRejectedCallbacks = [];

        const resolve = (value) => {
            if (this.state === 'pending') {
                this.state = 'fulfilled';
                this.value = value;
                // 一旦resolve执行,调用onResolvedCallbacks数组的函数
                this.onResolvedCallbacks.forEach(fn => fn());
            }
        };

        const reject = (reason) => {
            if (this.state === 'pending') {
                this.state = 'rejected';
                this.reason = reason;
                // 一旦reject执行,调用onRejectedCallbacks数组的函数
                this.onRejectedCallbacks.forEach(fn=>fn());
            }
        };

        try {
            executor(resolve, reject);
        } catch (e) {
            reject(e);
        }
    }

    then(onFulfilled, onRejected) {
        if (this.state === 'fulfilled') {
            onFulfilled(this.value)
        }

  
        if (this.state === 'rejected') {
            onRejected(this.reason)
        }

        // 状态为pending的时候,将onFulfilled、onRejected存入数组
        if (this.state === 'pending') {
            this.onResolvedCallbacks.push(() => {
                onFulfilled(this.value)
            })
            this.onRejectedCallbacks.push(() => {
                onRejected(this.reason)
            })
        }
    }
}
复制代码

实现链式调用

咱们经常会像下面代码同样使用Promise

new Promise()
    .then()
    .then()
    .then()
复制代码

这种方法叫作链式调用,一般是用来解决回调地狱(Callback Hell)的,就以下的代码:

fs.readdir(source, function (err, files) {
  if (err) {
    console.log('Error finding files: ' + err)
  } else {
    files.forEach(function (filename, fileIndex) {
      console.log(filename)
      gm(source + filename).size(function (err, values) {
        if (err) {
          console.log('Error identifying file size: ' + err)
        } else {
          console.log(filename + ' : ' + values)
          aspect = (values.width / values.height)
          widths.forEach(function (width, widthIndex) {
            height = Math.round(width / aspect)
            console.log('resizing ' + filename + 'to ' + height + 'x' + height)
            this.resize(width, height).write(dest + 'w' + width + '_' + filename, function(err) {
              if (err) console.log('Error writing file: ' + err)
            })
          }.bind(this))
        }
      })
    })
  }
})
复制代码

为了实现链式调用,咱们须要知足一下几点:

  • 咱们须要在then()返回一个新的Promise实例;
  • 若是上一个then()返回了一个值,则这个值就是onFulfilled()或者onRejected()的值,咱们须要把这个值传递到下一个then()中。

而对于上一个then()的返回值,咱们须要对齐进行必定的处理,所以封装一个resolvePromise()的方法去进行判断处理;

接下来咱们对then()方法进行修改:

class Promise {
    constructor(executor) { ... }

    /** * then 方法 * @returns {Promise<object>} * @param onFulfilled<function>: 状态为fulfilled时调用 * @param onRejected<function>: 状态为rejected时调用 */
    then(onFulfilled, onRejected) {
        // 返回一个新的Promise实例
        const newPromise = new Promise((resolve, reject) => {

            if (this.state === 'fulfilled') {
                const x = onFulfilled(this.value)

                // 对返回值进行处理 
                resolvePromise(newPromise, x, resolve, reject);
            }

            if (this.state === 'rejected') {
                const x = onRejected(this.reason);

                // 对返回值进行处理 
                resolvePromise(x, resolve, reject);
            }

            if (this.state === 'pending') {
                this.onResolvedCallbacks.push(() => {
                    const x = onFulfilled(this.value);

                    // 对返回值进行处理 
                    resolvePromise(newPromise, x, resolve, reject);
                })
                this.onRejectedCallbacks.push(() => {
                    const x = onRejected(this.reason);

                    // 对返回值进行处理 
                    resolvePromise(newPromise, x, resolve, reject);
                })
            }
        });
      
      	return newPromise;
    }
}

function resolvePromise() {}
复制代码

完成resolvePromise函数

对于上一个then()的返回值,咱们用x变量存起来,而后须要对它进行一个处理:

  • 判断x是否是Promise实例;
    • 若是是Promise实例,则取它的结果,做为新的Promise实例成功的结果;
    • 若是是普通值,直接做为Promise成功的结果;

而后咱们处理返回值后,须要利用newPromiseresolvereject方法将结果返回。

这里咱们还须要注意一个地方,就是x等于newPromise的话,这时会形成循环引用,致使死循环。

let p = new Promise(resolve => {
  resolve(0);
});
const p2 = p.then(data => {
  // 循环引用,本身等待本身完成,致使死循环
  return p2;
})
复制代码

所以,resolvePromise函数须要4个参数,即newPromisexresolvereject

因此咱们来实现一下resolvePromise函数:

/** * resolvePromise 方法 * @param newPromise<object>: 新的Promise实例 * @param x<any>: 上一个then()的返回值 * @param resolve<function>:Promise实例的resolve方法 * @param reject<function>:Promise实例的reject方法 */
function resolvePromise(newPromise, x, resolve, reject) {
    // 循环引用报错
    if(x === newPromise){
        // reject报错
        return reject(new TypeError('Chaining cycle detected for promise'));
    }
    // 防止屡次调用
    let called;
    if (x != null && (typeof x === 'object' || typeof x === 'function')) {
        try {
            let then = x.then;
            // x 为Promise实例
            if (typeof then === 'function') {
                // 使用call执行then(),call的第一个参数是this,后续即then()的参数,即第二个是成功的回调方法,第三个为失败的回调函数
                then.call(x, y => {
                    // 成功和失败只能调用一个
                    if(called)return;
                    called = true;
                    // resolve 的结果依旧是promise实例,那就继续解析
                    resolvePromise(newPromise, y, resolve, reject);
                }, err => {
                    // 成功和失败只能调用一个
                    if(called)return;
                    called = true;
                    // 失败了就直接返回reject报错
                    reject(err);
                })
            } else {
                // x 为普通的对象或方法,直接返回
                resolve(x);
            }
        } catch (e) {
            if(called)return;
            called = true;
            reject(e);
        }
    } else {
        // x 为普通的值,直接返回
        resolve(x);
    }
}
复制代码

onFulfilledonRejected

关于then()的两个参数——onFulfilledonRejected

  • 它们都是可选参数,并且它们都是函数,若是不是函数的话,就会被忽略掉;
    • 若是onFulfilled不是一个函数,就将它直接替换成函数value => value
    • 若是onRejected不是一个函数,就将它直接替换成函数err => {throw err};
class Promise {
    constructor(executor) { ... }

    then(onFulfilled, onRejected) {
        // onFulfilled若是不是函数,就忽略onFulfilled,直接返回value
        onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : value => value;
        // onRejected若是不是函数,就忽略onRejected,直接抛出错误
        onRejected = typeof onRejected === 'function' ? onRejected : err => { throw err };
        
      ...
    }
}
复制代码

其次,onFulfilledonRejected是不能同步被调用的,必须异步调用。所以咱们就用setTimeout解决一步问题。

class Promise {
    constructor(executor) { ... }

    then(onFulfilled, onRejected) {
        // onFulfilled若是不是函数,就忽略onFulfilled,直接返回value
        onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : value => value;
        // onRejected若是不是函数,就忽略onRejected,直接抛出错误
        onRejected = typeof onRejected === 'function' ? onRejected : err => {
            throw err
        };

        return new Promise((resolve, reject) => {
            if (this.state === 'fulfilled') {
                // 异步调用
                setTimeout(() => {
                    try {
                        const x = onFulfilled(this.value)
                        resolvePromise(x, resolve, reject);
                    }catch (e){
                        reject(e)
                    }
                })
            }

            if (this.state === 'rejected') {
                // 异步调用
                setTimeout(() => {
                    try{
                        const x = onRejected(this.reason);

                        resolvePromise(x, resolve, reject);
                    }catch (e){
                        reject(e)
                    }
                })
            }
          
            if (this.state === 'pending') {
                this.onResolvedCallbacks.push(() => {
                  // 异步调用
                    setTimeout(() => {
                        try {
                            const x = onFulfilled(this.value);
                            resolvePromise(x, resolve, reject);
                        }catch (e){
                            reject(e)
                        }
                    })
                })
                this.onRejectedCallbacks.push(() => {
                  // 异步调用
                    setTimeout(() => {
                        try {
                            const x = onRejected(this.reason);
                            resolvePromise(x, resolve, reject);
                        }catch (e){
                            reject(e)
                        }
                    })
                })
            }
        });
    }
}
复制代码

实现Promise的其余方法

Promise.all()

Promise.all()方法接收一个promiseiterable类型的输入,包括ArrayMapSet。而后返回一个Promise实例,该实例回调返回的结果是一个数组,包含输入全部promise的回调结果。

但只要任何一个输入的promisereject回调执行或者输入不合法的promise,就会立马抛出错误。

/** * Promise.all 方法 * @returns {Promise<object>} * @param promises<iterable>: 一个promise的iterable类型输入 */
Promise.all = function (promises) {
    let arr = [];

    return new Promise((resolve, reject) => {
       if (!promises.length) resolve([]);
        // 遍历promises
        for(const promise of promises) {
            promise.then(res => {
                arr.push(res);
                if(arr.length === promises.length){
                    resolve(arr);
                }
            }, reject)
        }
    })
}
复制代码

Promise.allSettled()

Promise.allSettled()其实跟Promise.all()很像,一样是接收一个promiseiterable类型的输入,但返回的是一个给定的promise已经完成后的promise,并带有一个对象数组,每一个对象标识着对应的promise结果。

const promise1 = Promise.resolve(3);
const promise2 = new Promise((resolve, reject) => setTimeout(reject, 100, 'foo'));
const promises = [promise1, promise2];

Promise.allSettled(promises).
  then((results) => console.log(results));
// > Array [Object { status: "fulfilled", value: 3 }, Object { status: "rejected", reason: "foo" }]
复制代码

实现:

/** * Promise.allSettled 方法 * @returns {Promise<object>} * @param promises<iterable>: 一个promise的iterable类型输入 */
Promise.allSettled = function (promises) {
    let arr = [];

    return new Promise((resolve, reject) => {
        try {
            const processData = (data) => {
                arr.push(data);
                if(arr.length === promises.length){
                    resolve(arr);
                }
            }

             if (!promises.length) resolve([]);
            // 遍历promises
            for(const promise of promises) {
                promise.then(res => {
                    processData({state:'fulfilled', value: res})
                }, err => {
                    processData({state:'rejected', reason: err})
                })
            }
        }catch (e){
            reject(e)
        }
    })
}
复制代码

Promise.any()

Promise.any()Promise.all()Promise.allSettled()同样,一样是接收一个promiseiterable类型的输入。但只要其中的一个promise成功,就返回那个已经成功的promise,但若是没有一个promise成功,就返回一个失败的promise`。

/** * Promise.any 方法 * @returns {Promise<object>} * @param promises<iterable>: 一个promise的iterable类型输入 */
Promise.any = function (promises) {
    return new Promise((resolve, reject) => {
        // 若是传入的参数是一个空的可迭代对象,则返回一个 已失败(already rejected) 状态的 Promise
        if (!promises.length) reject();
        // 若是传入的参数不包含任何 promise,则返回一个 异步完成 (asynchronously resolved)的 Promise。
        if (typeof promises[Symbol.iterator] !== 'function' ||
            promises === null ||
            typeof promises === 'string') {
            resolve()
        }

        let i = 0;
        // 遍历promises
        for (const promise of promises) {
            promise.then(res => {
                i++;
                resolve(res);
            }, err => {
                i++;
                if (i === promises.length) {
                    reject(err);
                }
            })
        }
    })
}
复制代码

Promise.race()

Promise.race(),一样是接收一个promiseiterable类型的输入。一旦迭代器中的某个promise完成了,无论是成功仍是失败,就会返回这个promise

/** * Promise.race 方法 * @returns {Promise<object>} * @param promises<iterable>: 一个promise的iterable类型输入 */
Promise.race = function (promises) {
    return new Promise((resolve, reject) => {
        for (const promise of promises) {
            promise.then(resolve, reject)
        }
    })
}
复制代码

Promise.reject()Promise.resolve()

Promise.reject()方法返回一个带有拒绝缘由的Promise对象;Promise.resolve()方法返回一个以定值解析后的Promise对象。

/** * Promise.reject 方法 * @returns {Promise<object>} * @param val<any> */
Promise.reject = function (val) {
    return new Promise(reject => reject(val))
}

/** * Promise.resolve 方法 * @returns {Promise<object>} * @param val<any> */
Promise.resolve = function (val) {
    return new Promise(resolve => resolve(val))
}
复制代码

catch()finally()

catch()方法是用来处理失败的状况,它传入一个处理函数,而后返回一个promise实例。实际上它是then()的语法糖,只接受rejected态的数据。

finally()是在promise结束时,不管结果是fufilled仍是rejected,都会执行指定的回调函数。一样也返回一个promise实例。

class Promise {
   	constructor(executor) { ... }

    then(onFulfilled, onRejected) { ... }

    /** * catch 方法 * @returns {Promise<object>} * @param callback<function>: 处理函数 */
    catch(callback) {
        return this.then(null, callback);
    }

    /** * finally 方法 * @returns {Promise<object>} * @param callback<function>: 处理函数 */
    finally(callback) {
        return this.then(res => {
            return Promise.resolve(callback()).then(() => res)
        }, err => {
            return Promise.reject(callback()).then(() => {
                throw err
            })
        })
    }
}
复制代码

Promise/A+测试

Promise/A+规范: github.com/promises-ap…

Promise/A+测试工具: github.com/promises-ap…

安装promises-aplus-tests插件。

yarn add promises-aplus-tests
复制代码

Promise.js后面插入下列代码。

// 测试
Promise.defer = Promise.deferred = function () {
    let dfd = {}
    dfd.promise = new Promise((resolve,reject)=>{
        dfd.resolve = resolve;
        dfd.reject = reject;
    });
    return dfd;
}
module.exports = Promise;
复制代码

而后输入命令行进行测试。

promises-aplus-tests Promise.js
复制代码

结果:

872 passing (18s)
复制代码
相关文章
相关标签/搜索