Promise 简朴完成

Promise 简朴完成

媒介

你能够晓得,javascript 的使命实行的形式有两种:同步和异步。

异步形式非常重要,在浏览器端,耗时很长的操纵(比方 ajax 要求)都应该异步实行,防止浏览器落空相应。

在异步形式编程中,我们常常运用回调函数。一不小心就能够写出以下如许的代码:

//事宜1
doSomeThing1(function() {
    //事宜2
    doSomeThing2(function() {
        //事宜3
        doSomeThing3();
    });
});

当你的须要异步实行的函数越来越多,你的层级也会越来越深。

如许的写法存在的瑕玷是:

  1. 不利于浏览
  2. 各个使命之间的高度耦合,难以保护
  3. 对非常的处置惩罚比较难

用 Promise 能够防止这类回调地狱,能够写成如许

//事宜1
doSomeThing1()
    .then(function() {
        //事宜2
        return doSomeThing2();
    })
    .then(function() {
        //事宜3
        return doSomeThing3();
    })
    .catch(function() {
        //这里能够很轻易的做非常处置惩罚
    });

在市情上有很多库都完成了 Promise,比方:Q.js 、when.js ,es6 也将 Promise 纳入了规范中

es6 的 Promise 运用要领能够参考阮一峰的
http://es6.ruanyifeng.com/#do… ,我就不在做详细引见

接下来,我们模仿 ES6 的 promise,一步一步来完成一个简朴版的 Promise。

组织函数

我们运用 Promise 的时刻,

const promise = new Promise((resolve, reject)=>{
  // ... some code
  if (/* 异步操纵胜利 */){
    resolve(value);
  } else {
    reject(error);
  }
});

Promise 是一个组织函数,吸收一个函数,函数里有两个参数,resolve、reject。

我们能够如许子完成:

class PromiseA {
    constructor(executor) {
        const resolve = value => {
            this.resolve(value);
        };
        const reject = err => {
            this.reject(err);
        };
        try {
            executor(resolve, reject);
        } catch (err) {
            reject(err);
        }
    }

    resolve(value) {
        this.resultValue = value;
    }

    reject(error) {
        this.resultValue = error;
    }
}

then 要领

promise 中,用的最频仍的就是 then 要领,then 要领有两个参数,一个是 promise 胜利时刻的回调,一个是失利的回调。

完成体式格局为:

class PromiseA {
    constructor(executor) {
        const resolve = value => {
            this.resolve(value);
        };
        const reject = err => {
            this.reject(err);
        };
        try {
            executor(resolve, reject);
        } catch (err) {
            reject(err);
        }
    }

    resolve(value) {
        this.resultValue = value;
        if (this.fullfillCallback) {
            //++++++++
            this.fullfillCallback(value);
        }
    }

    reject(error) {
        this.resultValue = error;
        if (this.rejectCallback) {
            //++++++++
            this.rejectCallback(value);
        }
    }

    then(resolve, reject) {
        this.fullfillCallback = resolve;
        this.rejectCallback = resolve;
    }
}

then 要领有以下几个特征:

  1. 支撑链式操纵
  2. 每次 then 要领都是返回新的 Promise
  3. 当前 promise 的状况经由过程返回值传递给下一个 promise
  4. 毛病冒泡,即假如当前 promise 没有供应 onReject 要领,会把毛病冒泡到下一个 promise,轻易处置惩罚
then(onResolve,onReject){

  //返回新的Promise并支撑链式操纵
  return new PromiseA((resolve,reject)=>{

    this.fullfillCallback = (value)=>{
        try {
            if (onResolve) {
                let newValue = onResolve(value);
                resolve(newValue); //将当前promise实行效果,传递给下一个promise
            } else {
                resolve(value);
            }
        } catch (err) {
            reject(err);
        }
    }

    //相似fullfillCallback
    this.rejectCallback = (value)=>{
          try {
              if (onReject) {
                  let newValue = onReject(value);
                  resolve(newValue);
              } else {
                  //毛病冒泡
                  reject(value);
              }
          } catch (err) {
              reject(err);
         }
    }
  });
}

如许我们就完成了一个简朴版的 then 要领了

加强版 then

上面的完成,模仿了 then 要领的逻辑,然则另有一些瑕玷:

  1. then 要领只能增添一个,比方
let p = new PromiseA(resolve => {
    setTimeout(() => {
        resolve(1);
    }, 0);
});
p.then(value => {
    console.log('then-->' + value);
}); //无输出,由于没触发到,被后一个覆蓋了
p.then(value => {
    console.log('then2-->' + value);
}); ////then---> 1
  1. promise 没有状况,当 promsie 在增添 then 的时刻已完成了,没法获得效果
  2. 没有完成:假如上一个 promise 的返回值也是一个 Promise 对象时,则会比及这个 Promise resolve 的时刻才实行下一个

为了处理第一点,引入了事宜监听,简朴的完成以下:

export default class EventEmitter {
    constructor() {
        this._events = {};
    }

    on(type, fn, context = this) {
        if (!this._events[type]) {
            this._events[type] = [];
        }
        this._events[type].push([fn, context]);
    }

    trigger(type) {
        let events = this._events[type];
        if (!events) {
            return;
        }
        let len = events.length;
        let eventsCopy = [...events];
        for (let i = 0; i < len; i++) {
            let event = eventsCopy[i];
            let [fn, context] = event;
            if (fn) {
                fn.apply(context, [].slice.call(arguments, 1));
            }
        }
    }
}

所以进一步对 PromiseA 举行革新:

const STATUS = {
    PENDING: 'pending',
    FULFILLED: 'fulfilled',
    REJECTED: 'rejected'
};

const EventType = {
    fulfill: 'fulfill',
    reject: 'reject'
};

class PromiseA {
    constructor(executor) {
        //初始化事宜监听及状况
        this.eventEmitter = new EventEmitter();
        this.status = STATUS.PENDING;

        const resolve = value => {
            if (value instanceof PromiseA) {
                value.then(
                    value => {
                        this.resolve(value);
                    },
                    error => {
                        this.reject(error);
                    }
                );
            } else {
                this.resolve(value);
            }
        };
        const reject = err => {
            this.reject(err);
        };
        try {
            executor(resolve, reject);
        } catch (err) {
            reject(err);
        }
    }

    resolve(value) {
        //增添状况
        if (this.status === STATUS.PENDING) {
            this.status = STATUS.FULFILLED;
            this.resultValue = value;
            this.eventEmitter.trigger(EventType.fulfill, value);
        }
    }

    reject(error) {
        //增添状况
        if (this.status === STATUS.PENDING) {
            this.status = STATUS.REJECTED;
            this.resultValue = error;
            this.eventEmitter.trigger(EventType.reject, error);
        }
    }

    then(onResolve, onReject) {
        //依据状况差别处置惩罚
        if (this.status === STATUS.PENDING) {
            return new PromiseA((resolve, reject) => {
                //增添事宜监听
                this.eventEmitter.on('fulfill', value => {
                    try {
                        if (onResolve) {
                            let newValue = onResolve(value);
                            resolve(newValue);
                        } else {
                            resolve(value);
                        }
                    } catch (err) {
                        reject(err);
                    }
                });
                //增添事宜监听
                this.eventEmitter.on('reject', value => {
                    try {
                        if (onReject) {
                            let newValue = onReject(value);
                            resolve(newValue);
                        } else {
                            reject(value);
                        }
                    } catch (err) {
                        reject(err);
                    }
                });
            });
        }
        if (
            this.status === STATUS.FULFILLED ||
            this.status === STATUS.REJECTED
        ) {
            return new PromiseA((resolve, reject) => {
                let callback = returnValue;
                if (this.status === STATUS.FULFILLED) {
                    callback = onResolve;
                }
                if (this.status === STATUS.REJECTED) {
                    callback = onReject;
                }
                try {
                    let newValue = callback(this.resultValue);
                    resolveValue(newValue, resolve, reject);
                } catch (err) {
                    reject(err);
                }
            });
        }
    }
}

到这里,我们的 then 要领基本就完成了。

末了另有一个小知识点,就是实行机遇的题目:

setTimeout(function() {
    console.log(4);
}, 0);
new Promise(function(resolve) {
    console.log(1);
    resolve();
}).then(function() {
    console.log(3);
});
console.log(2);
//输出效果会是: 1、2、3、4

promise.then,是异步的,属于 microtask,实行机遇是本次事宜轮回完毕之前,而 setTimeout 是 macrotask,实行机遇是鄙人一次事宜轮回的最先之时

完成这个功用,我利用了第三方库 microtask 来模仿。所以 PromiseA 修改成:

    resolve(value) {
        microtask(() => {
            if (this.status === STATUS.PENDING) {
                this.status = STATUS.FULFILLED;
                this.resultValue = value;
                this.eventEmitter.trigger(EventType.fulfill, value);
            }
        })
    }

    reject(error) {
        microtask(() => {
            if (this.status === STATUS.PENDING) {
                this.status = STATUS.REJECTED;
                this.resultValue = error;
                this.eventEmitter.trigger(EventType.reject, error);
            }
        });
    }

到此为止,我们的 then 要领已功德圆满了。最难题的一步已处理了

catch

Promise 跟一般回调的一大上风就是非常处置惩罚,我们引荐运用 Promise 的时刻,老是运用 catch 来替代 then 的第二个参数。等于:

//bad
let p = new Promise((resolve, reject) => {
    //...异步操纵
}).then(
    value => {
        //胜利
    },
    () => {
        //失利
    }
);

//good
let p = new Promise((resolve, reject) => {
    //...异步操纵
})
    .then(value => {
        //胜利
    })
    .catch(() => {
        //失利
    });

接下来让我们完成 catch 要领:

catch(reject) {
   return this.then(null, reject);
}

哈哈~ , 你没看错。你已完成了 catch 要领

all 要领

Promise.all 是一个很好用的要领。接收一个 promise 数组,然后比及一切的异步操纵都完成了,就返回一个数组,包括对应的值

详细完成以下:

static all(promiseList = []) {
      //返回promise以便链式操纵
    return new PromiseA((resolve, reject) => {
        let results = [];
          let len = promiseList.length;
        let resolveCount = 0; //用于计数

        let resolver = function (index, value) {
            resolveCount++;
            results[index] = value;
            if (resolveCount === len) {
                resolve(results);
            }
        };

          //遍历实行一切的promise
        promiseList.forEach((p, i) => {
            if (p instanceof PromiseA) {
                p.then((value) => {
                    resolver(i, value);
                }, (err) => {
                    reject(err);
                })
            } else {
                resolver(i, p);
            }
        })
    });
}

race 要领

race 要领为竞速,第一实行完的为准。所以只需轮回一遍实行就能够了。

当有第一个将 Promise 的状况改变成 fullfilled 或 reject 以后,其他的就都无效了

static race(promiseList = []) {
   return new PromiseA((resolve, reject) => {
      promiseList.forEach((p, i) => {
         if (p instanceof PromiseA) {
            p.then((value) => {
               resolve(value);
            }, (err) => {
               reject(err);
            })
         } else {
            resolve(p);
         }
      })
   })
}

小结

我们完成了一个简朴版的 promise, 另有一些其他的要领在这里没有讲到。感兴趣的朋侪能够自行去研讨哈~

附上代码完全的完成 :
https://github.com/chen434202…

个人博客链接:https://chen4342024.github.io…

    原文作者:chen4342024
    原文地址: https://segmentfault.com/a/1190000017458611
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞