github

博客

Promise的申明

当咱们应用Promise的时候,通常都是new Promise((resolve, reject) => {})

因而咱们能够看出:

  • Promise是一个类;
  • Promise类的构造函数的第一个参数是函数,这个函数叫处理器函数(executor function);
  • 而在处理器函数中,有了两个参数:resolvereject

    - 当异步工作顺利完成且返回后果值的时候,咱们会调用`resolve`函数;- 当异步工作失败且返回失败起因(通常是一个谬误对象)时,会调用`reject`函数。

因而,咱们能够初步申明一下Promise类。

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的值。

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办法,而该办法中有两个参数:onFulfilledonRejected

  • 这两个参数都是一个函数,且会返回一个后果值;
  • 当状态为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实际上一个异步操作:

  • 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+标准: https://github.com/promises-a...

Promise/A+测试工具: https://github.com/promises-a...

装置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)