Promise

  • 其实就是一个类,外部保留着回调队列,通过裸露resolve和reject办法触发对应回调
  • 外部有三种状态值,pending,fulfiled,failed
  • Promise是微工作,与settimout等宏工作有差异,执行程序时是,先主线程,而后微工作,而后宏工作
  • 有以下几种办法:then, catch, all, race, resolve, reject
  • 一旦实例了一个Promise,其就肯定会执行,无奈进行
  • Promise中,因为报错也会触发reject,所以不会中断程序
  • catch其实是then的语法糖,相当于then(null,rej),即胜利回调传了空
  • 如果then中,没有传入回调,则promise的resolve或reject值会传递到下一个then或catch中,如下:
const p = new Promise((res) => res(2));p.then(null).then((data) => console.log(data)); // 2p.then(() => {}).then((data) => console.log(data)); // undefinedp.catch((data) => {console.log('catch', data)}).then((data) => console.log('then', data)); // then, 2

实现

  • 外部变量
function myPromise(func) {    this.status = 'pending'; // 状态    this.value = undefined; // 值    this.resolveList = []; // 胜利回调    this.rejectList = []; // 失败回调}
  • 外部resolve和reject办法
  • 同时执行传入的func
function myPromise(func) {    this.status = 'pending'; // 状态    this.value = undefined; // 值    this.resolveList = []; // 胜利回调    this.rejectList = []; // 失败回调        const resolve = (data) => {        queueMicrotask(() => { // 新建微工作            if (this.status === 'pending') {                this.status = 'fulfiled';                this.value = data;                this.resolveList.forEach((resFunc) => resFunc(data));                this.resolveList.length = 0;            }        });    };        const reject = (data) => {        queueMicrotask(() => { // 新建微工作            if (this.status === 'pending') {                this.status = 'failed';                this.value = data;                this.rejectList.forEach((resFunc) => resFunc(data));                this.rejectList.length = 0;            }        });    };        if (typeof func === 'function') {        try {            func(resolve, reject); // promise传入的func是马上执行的,但回调resolve和reject是异步微工作        } catch(err) {            reject(err);        }    }}
  • then办法, 定义在原型链上, 返回一个promise
  • 分三种状态解决,pending时仅把回调传入同时promise化,fulfilled或failed时间接执行,并promise返回
  • 这里能够看出下面形容的,如果resCb或rejCb传入为空,会将promise的value传递给返回的promise
  • 并且如果cb返回的是promise,会以那个promise的res或rej触发下层的res或rej,同时cb中的data值是内层promise的value
myPromise.prototype.then = function (resCb, rejCb) {    if (this.status === 'pending') {        return new myPromise((res, rej) => {            this.resolveList.push(function () {                // 如果没有定义回调,将值传递给下一个                try {                    const ret = typeof resCb === 'function' ? resCb(this.value) : this.value;                    if (ret instanceof myPromise) {                        ret.then(res, rej);                    } else {                        res(ret);                    }                   } catch (err) {                    rej(err);                }            });            this.rejectList.push(function () {                // 如果没有定义回调,将值传递给下一个                try {                    const ret = typeof rejCb === 'function' ? rejCb(this.value) : this.value;                    if (ret instanceof myPromise) {                        ret.then(res, rej);                    } else {                        rej(ret);                    }                   } catch (err) {                    rej(err);                }            });        });    } else if (this.status === 'fulfiled) {        return new myPromise((res, rej) => {            try {                const ret = typeof resCb === 'function' ? resCb(this.value) : this.value;                if (ret instanceof myPromise) {                    ret.then(res, rej);                } else {                    res(ret);                }            } catch (err) {                rej(err)            }        });    } else if (this.status === 'failed') {        return new myPromise((res, rej) => {            try {                const ret = typeof rejCb === 'function' ? rejCb(this.value) : this.value;                if (ret instanceof myPromise) {                    ret.then(res, rej);                } else {                    rej(ret);                }            } catch (err) {                rej(err)            }        });    }};
  • catch办法,即then的语法糖
myPromise.prototype.catch = (rej) => myPromise.prototype.then(null, rej);
  • all办法,传入一个数组,输入一个promise,全副resolve时,输入promise状态变为fulfiled,触发resolve
  • 当任何一个传入的promise失败时,输入promise状态变failed,触发reject,但并不会终止其余promise继续执行,只是其余promise的执行曾经不会影响promise.all返回promise的状态了
myPromise.prototype.all = (iterable) => {    const len = iterable.length;    const result = [];    return new myPromise((resolve, reject) => {        let index = 0;        iterable.forEach((item) => {            item.then((data) => {                result.push(data);                index++;                if (index === len) resolve(result);            }, (err) => {                reject(err);            });        });    });}
  • race办法,返回promise数组中第一个执行结束的状态,无论是fulfiled还是failed,但不影响数组中其余promise继续执行
myPromise.prototype.race = (iterable) => {    let done = false;    return new myPromise((resolve, reject) => {        iterable.forEach((item) => {            item.then((data) => {                if (!done) {                    done = true;                    resolve(data);                }            }, (err) => {                if (!done) {                    done = true;                    reject(err);                }            });        });    });}
  • 其中下面的done标记能够去掉,因为promise中,resolve两次,只会在首次执行then函数,具体起因是,第一次resolve时,执行resolveList中的回调函数,执行结束后会把其清零,即如果没有在执行结束后再注册新的then办法,第二次resolve不会触发回调
  • 另外即便在第二次reslove后新注册了一个then,其获取的值也是第一次resolve的值,而不是第二次resolve的值,这是因为更新promise内的value只会产生在promise状态从pending变为fulfiled或failed的时候,而第一次resolve曾经让状态变了,不会再触发value的更新,例子如下:
const p1 = new Promise((res) => {    setTimeout(() => { console.log('res(1)'); res(1)}, 1000);    setTimeout(() => { console.log('res(2)'); res(2)}, 3000);});p1.then((data) => console.log(data));setTimeout(() => {    console.log('then again');    p1.then((data) => console.log(data));}, 5000);/* 输入 res(1) -> 1  -> res(2) -> then again -> 1最初输入1而不是2*/
  • 回到race,上述race办法可简化写成如下
myPromise.prototype.race = (iterable) => {    return new myPromise((resolve, reject) => {        iterable.forEach((item) => {            item.then(resolve, reject);        });    });}
  • reslove和reject办法,其实就是裸露外部定义的reslove和reject
myPromise.prototype.reslove = (data) => {    return new myPromise((resolve, reject) => {        resolve(data);    });};myPromise.prototype.reject = (data) => {    return new myPromise((resolve, reject) => {        reject(data);    });};