3

引言

16年时在公司分享过一次promise,犹记得当时是第一次分享,还蛮紧张的,当时分享的主要是promise的使用和基本原理,后来又给无线部门同学分享了一次。
现在回顾想想,其实讲的不是很完美,因为我当时的实现方式类似于简化版q库的实现,考虑的也不全面,也没有完全遵循promise/a+规范。经过这么长一段时间的学习和积累,阐述一下自己新的理解。

Promise出现的起因

在没有promise以前,多个有依赖的异步操作一般写出来会出现嵌套,所谓的回调地域,这种写法在需要协作的项目中不方便维护,异步操作也不能直接捕获异常,需要回调中进行处理,缺点挺多的,然后就开始漫长的优化,出现了q, bluebird,jq中的defer等这些库,后来ES6标准实现了Promise,但是其链式写法还是不美观,为了代码更优雅,可以视觉上同步命令式的书写代码有了TJ大神的co再结合generator似乎完美了,但是为了优雅还要额外引入co库,成本有点大,后来ES7标准干脆直接实现了,就是所谓的async和await语法糖

Promise定义

现在开始切入正题,什么是Promise呢? 简而言之promise代表承诺,专业术语就是代表一个异步操作的最终结果。
代码层面来看的话Promise是一个类,可以用来创建实例,每个实例内部封装一些方法,且维护了一些状态和值,通过使用这些状态、值和方法来将现实流程中的承诺具体代码化表示。

Promise使用

promise主要提供了then,catch,all,race,resolve,reject这几个方法,关于这几个方法怎么使用不在赘述,因为占据文章篇幅长,且很多其它blog重复描述过。推荐阮一峰es6入门中相关api用法解释,详细且全面。
关于具体应用的话,由于在工作中项目基于vue技术栈,所以结合axios时会使用到promise来操作异步,还有就是m站基于pwa,其中Service worker声明周期事件处理中会涉及promise,还有一些就是平时写node工具的时候会用到,用promise封装异步api操作回调,从而将异步api回调逻辑直接放到then方法中进行处理。

Promise的实现

基于Promise/a+规范实现的代码能互相统一,虽然代码形式会有不同,但原理都差不多。
首先Promise构造函数中需要有一些状态和方法,因为执行实例then逻辑的时候需要这些维护好的状态和值,其中着重提醒的就是promise的状态机是单向的,且状态单向不可逆。
状态转变只能是 pending -> fulfilled 或者 pending -> rejected。

      //构造函数初始化逻辑
     let that = this; //缓存this
    //默认状态为pending
    that.status = 'pending';
    //此变量里放着此promise的结果
    that.value = undefined;
    //存放的着所有成功的回调函数
    that.onResolvedCallbacks = [];
    //存放着所有的失败的回调函数
    that.onRejectedCallbacks = [];

其中内部resolve和reject逻辑如下,更改状态机状态,触发承诺逻辑执行

  function resolve(value) {
        //更改状态 执行then注册的成功回调逻辑
        if (that.status == 'pending') {
            //解决resolve 新Promise这种情况
            if(value!=null &&value.then&&typeof value.then == 'function'){
              return value.then(resolve,reject);
            }
            that.status = 'fulfilled';
            that.value = value;
            that.onResolvedCallbacks.forEach(item=>item(that.value));
        }
    }
     function reject(reason) {
            //更改状态 执行then注册的失败回调逻辑或者catch中注册的失败逻辑
            if (that.status == 'pending') {
                that.status = 'rejected';
                that.value = reason;
                that.onRejectedCallbacks.forEach(item=>item(that.value));
            }
        }

上面已经介绍了大致初始化逻辑了,下面着重介绍使用频率最高的then方法,简洁版实现如下所示

PPromise.prototype.then = function (onFulfilled, onReject) {
    //成功和失败的逻辑没有传递 会进行值的穿透 传递给下一个then方法
     onFulfilled = isFunction(onFulfilled) ?onFulfilled:val =>val;
     onReject = isFunction(onReject) ?onReject:reason => {throw reason;}
     let self = this,promise2;
     switch (self.status){
         case 'fulfilled':
             promise2 = new Promise((resolve,reject) =>{
                 let x = onFulfilled(self.value);
                 if(x instanceof Promise){
                    //递归执行then逻辑 直到内部then执行,外部promise2被resolve
                     x.then(resolve,reject)
                 }else{
                     resolve(x);
                 }
             });
             break
         case 'rejected':
             promise2 = new Promise((resolve,reject) =>{
                 let x = onReject(self.value);
                 if(x instanceof Promise){
                     x.then(resolve,reject)
                 }else{
                     resolve(x);
                 }
             })
             break
         case 'pending':
             promise2 = new Promise((resolve,reject) =>{
                 self.onResolvedCallbacks.push(function(){
                     let x = onFulfilled(self.value);
                     if(x instanceof Promise){
                         x.then(resolve,reject)
                     }else{
                         resolve(x);
                     }
                 });
                 self.onRejectedCallbacks.push(function(){
                     let x = onReject(self.value);
                     if(x instanceof Promise){
                         x.then(resolve,reject)
                     }else{
                         resolve(x);
                     }
                 });
             });
     }
     return promise2;
 }

all方法实现

function sentry(times,cb){
  let result = [],count=0;
  return function(i,data){
    result[i] = data;
    if(++count==times){
      cb(result);
    }
  }
}
Promise.all = function(promises){
 return new Promise((resolve,reject) => {
   //利用闭包机制,目的是为了判断promises是否都执行完
   let done = sentry(promises.length,resolve);
   for(let i=0;i<promises.length;i++){
     promises[i].then(data =>{
       done(i,data);
     },reject);
   }
 });
}

resolve实现

Promise.resolve = function(value){
  return new Promise(function(resolve){
    resolve(value);
  });
}

race实现

Promise.race = function(promises){
  return new Promise((resolve,reject) =>{
    for(let i=0;i<promises.length;i++){
      promises[i].then(resolve,reject);
    }
  });
}

自己实现的promise源码

异步优雅写法

异步操作经过promisify转化成promise,在结合async实现优雅的写法

let Promise = require('bluebird');
let readFile = Promise.promisify(require('fs').readFile);
async function read() {
  let a = await readFile('./1.txt','utf8');
  let b = await readFile('./2.txt','utf8');
  let c = await readFile('./3.txt','utf8');
  console.log(c);
  return 'ok';
}

read().then(data => {
  console.log(data);
});

总结

任何事物都不是一蹴而就的,都有一个发展过程才逐步变得完美,将自己的学习坐下记录,并加一些个人思考,如果对于本文有任何疑问或错误,欢迎斧正交流。

参考链接
https://promisesaplus.com/
https://segmentfault.com/a/11...


jsdt
4.9k 声望3.9k 粉丝

make a little progress every day