阅读 8731

从手写Promise到async/await(接近6千字,建议看一下)

导言:在学习 async/awit之前, 我们很有必要学习一下 , 迭代器(lterator)生成器(Generator)

迭代器(lterator)和生成器(Generator)

导言:用循环语句迭代数据时,必须要初始化有关变量来记录每一次迭代在数据集合中的位置, 迭代器的使用可以极大地简化数据操作,于是es6也向js中添加了这个迭代器特性。新的数组方法和新的集合类型(例如:Set与Map集合)都依赖迭代器的实现,甚至异步编程中都可以使用迭代器。
但这此之前,我们一定要了解一下迭代器的背后的历史

循环语句的问题

var colors = ['red','green','blue'];
for(var i = 0 ,len = colors.length ; i < len ; i ++) {
    console.log(colors[i])
}
复制代码

上面是一段标准的for循环代码,通过变量i来跟踪colors数组的索引。
虽然循环语句的语法简单,但是如果将多个循环嵌套则需要追踪多个变量,代码的复杂度会大大增加。迭代器的出现为了消除这种复杂性并减少循环中的错误。

什么是迭代器

迭代器是一种特殊对象,它具有一些专门为迭代过程设计的专有接口,所有的迭代器对象都有一个next()方法,每次调用都返回一个结果对象。结果对象有两个属性:一个是value,表示下一个将要返回的值;另一个是done, 它是一个布尔类型的值,当没有更多可返回数据时返回数据时返回true。迭代器还会保存一个内部指针,用来指向当前集合中值的位置,每调用一次next()方法,都会返回下一个可用的值。
我们用es5的语法来自己写一个迭代器
按我的理解,这应该属于闭包的用处

function createIterator(items) {
var i = 0;
    return {
        next :function() {
            var done = ( i >= items.length);
            var value = !done ? items[i++] :undefined;
            return {
                done : done,
                value : value
            }
        }
    }
}

var iterator = createIterator([1,2,3]);
console.log(iterator.next()) //{ done: false, value: 1 }
console.log(iterator.next()) //{ done: false, value: 2 }
console.log(iterator.next()) //{ done: false, value: 3 }
console.log(iterator.next()) //{ done: true, value: undefined }
console.log(iterator.next()) //{ done: true, value: undefined }
复制代码

迭代器的编写规则也同样复杂,但es6同时还引入一个生成器对象,它可让创建迭代器对象过程变得简单。
note: 检测对象是否为可迭代对象

    function isIterable(object) {
        return typeof object[Symbol.iterator] === "function";
    }
复制代码

什么是生成器

生成器是一种返回迭代器的函数,通过 function 关键字后的 星号(*)来表示, 函数中会用到新的关键字 yield 。就像这样 :

//生成器
function *createIterator() {
    yield 1;
    yield 2;
    yield 3;
}
// 生成器的调用方式与普通函数相同, 只不过返回的是一个迭代器
let iterator = createIterator();
console.log(iterator.next())//{ value: 1, done: false }
console.log(iterator.next())//{ value: 2, done: false }       
console.log(iterator.next())//{ value: 3, done: false }
console.log(iterator.next())//{ value: undefined, done: true }
复制代码

在这个实例中, createIterator()前的星号表明它是一个生成器;yield 关键字也是 es6的新特性 , 可用通过它来指定调用迭代器的 next()方法时的返回值及返回顺序。生成迭代器后, 连续3次调用它的next()方法返回3个不同的值 , 分别是 1,2,3。 生成器的调用过程与其他函数一样 , 最终返回的是创建好的迭代器。
生成器函数最重要的是,每当执行完一条 yield 语句后函数就会自动停止执行.

异步任务执行

生成器令人兴奋的特性多与异步编程有关, javaScript中的异步编程有利有弊:简单任务的异步化非常容易实现;而复杂任务的异步化会带来很多管理代码的挑战。由于生成器支持在函数中暂停代码执行,因而可以深入挖掘异步处理的更多用法。如果需要嵌套回调化序列化一系列的异步操作,事情会变得非常复杂。此时,生成器和yield语句就派上用场了。

简单任务执行器

由于执行yield 语句会暂停当前函数的执行过程并等待下一次调用 next()方法。因此你可以创建一个函数,在函数中调用生成器生成相应的迭代器,从而在不用回调函数的基础上实现异步调用next()方法,就像这样:

function run(taskDef) {
    // 创建一个无使用限制的迭代器
    let task = taskDef();
    // 开始执行任务
    let result = task.next();
    //循环调用next()的函数
    function step() {
        // 如果任务未完成,则继续执行
        if(!result.done) {
            result = task.next();
            step();
        }
    }
    //开始迭代执行
    step();
}
// 执行
run(function *() {
    console.log(1);
    yield;
    console.log(2);
    yield;
    console.log(3)
})
复制代码

函数run()接收一个生成器函数作为参数,这个函数定义了后续要执行的任务,生成一个迭代器并将它存储其在变量task中。首次调用迭代器的next()方法时,返回的结果被存储起来稍后继续使用。step()函数会检查result.done的值,如果为false则执行迭代器的next()方法,并再次执行step()操作。每次调用next()方法,返回的最新信息总是覆盖变量result。在代码的最后,初始化执行step()函数并开始整个的迭代过程,每次通过检查result.done来确定是否有更多任务需要执行。

向任务执行器传递数据

给任务执行器传递数据的最简单方法是,将值通过迭代器的next()方法传入作为yield的生成值供下次调用。这下面这段代码中,只需要将result.value传入next()方法即可:

function run(taskDef) {
    // 创建一个无使用限制的迭代器
    let task = taskDef();
    //开始执行任务
    let result = task.next();
    // 循环调用next()的函数
    function step() {
        //如果任务未完成 , 则继续执行
        if(!result.done) {
            result = task.next(result.value);
            step();
        }
    }
    //开始迭代执行
    step();
}

run(function *() {
    let value = yield 1;
    console.log(value); // 1

    value = yield value + 3;
    console.log(value);
})
复制代码

这例子会向控制台输出两个数值 1 和 4.
现在数据已经能够在yield调用间互相传递了,只需要一个小小的改变便能支持异步调用。

异步任务执行器

之前的例子只是在多个yield调用间来回传递静态数据,而等待一个异步过程有些不同。任务执行器需要知晓回调函数是什么以及如何使用它。由于 yield表达式会将值返回给任务执行器,所有的函数调用都会返回一个值,因此在某种程度上这也是一个异步操作,任务执行器会一直等待直到操作完成。
下面我们定义一个异步操作:

        function fetchData() {
            return function(callback) {
                setTimeout(function() {
                    callback(null,'Hi');
                }, 50)
            }
        }
复制代码

理解了函数中异步过程的运作方式,我们可以将任务执行器稍作修改。当result.value是一个函数时,任务执行器会先执行这个函数再讲结果传入next()方法,代码更新如下 :

function run(taskDef) {
    // 创建一个无使用限制的迭代器
    let task = taskDef();
    //开始执行任务
    let result = task.next();
    // 循环调用next()函数
    function step() {
        //如果任务未完成 , 则继续执行
        if(!result.done) {
            if(typeof result.value === "function") {
                result.value(function(err,data) {
                    if(err) {
                        result = task.throw(err);
                        return;
                    }
                    result = task.next(data);
                    step();
                });
            } else {
                result = task.next(result.value);
                step();
            }
        }
    }
    //开始执行迭代
    step();
}
复制代码

通过 === 操作符检查后 , 如果 result.value 是一个函数,会传入一个回调函数作为参数来调用它,回调函数遵循node.js中有关执行错误的约定;所可能的错误放在第一个参数(err)中,结果放在第二个参数中。如果传入了err,则意味着执行过程中产生了cuow,这时会通过task.throw()正确输出错误对象。如果 result.value 不是一个函数,则直接将其传入next()方法。
现在,这个新版的任务执行器已经可以用于所有的异步任务了。在node.js环境中,如果要从文件中读取一些数据,需要在fs.readFile()外围创建一个包装器(wrapper), 返回一个与 fetchData()类似的函数

    let fs = require('fs');
    function readFile(filename) {
        return function(callback) {
            fs.readFile(filename,'utf8',callback);
        }
    }
    //调用
    run(function *(){
        let contents = yield readFile('1.txt');
        console.log(contents);
    })
复制代码

Promise与异步编程

哎,我们终于开始学习Promise了,接一来,我们将学

  • Promise出现的原因是什么
  • Promise是什么
  • Promise的基本概念
  • 最后我们将亲自手写一个简单的Promise

异步编程背景知识

javaScript引擎是基于单线程和事件循环的概念构建的,同一时刻只允许一个代码块执行。

Promise出现的原因

那么,如果我们有一天必须控制 两段异步操作的执行顺序时,我们该怎么办呢?
比如,我们必须先 读1.txt文件 , 再复制一份 2.txt文件。 我们知道 , I/O操作都是异步的 , 控制顺序的话,我们可以这样:

fs.readFile('./1.txt','utf8',(err,data)=>{
    fs.writeFile('./2.txt',data,(err)=>{
        if(err) {
            return;
        }
    })
})
复制代码

简单来说,就是把写的操作放在读的里面来执行。但这样不太好,因为一旦要操作异步的顺序的代码多了起来,一旦不知道哪里出了 bug ,就会很难调试,看的头都会大

Promise是什么

Promise是一种异步操作的解决方案,将写法复杂的传统的回调函数和监听事件的异步操作,用同步代码的形式表达出来。避免了多级异步操作的回调函数嵌套。

Promise的基本概念

Promise相当于异步操作结果的占位符,它不会订阅一个事件,也不会传递一个回调函数给目标函数,而是让函数返回一个Promise

Promise的生命周期

每个Promise都会经历一个短暂的生命周期 :先是处于进行中(pending)的状态,此时操作尚未完成,所以它也是未处理(unsettled)的;一但异步操作执行结束,Promise在变成下面其中一个:

  • Fulfilled Promise异步操作成功完成
  • Rejected 由于程序错误或一些其他原因,Promise异步操作未能成功完成。

note:如果一个对象实现了then()方法,那这个对象我们称之为 thenable 对象。所有的Promise都是thenable对象,但并非所有thenable对象都是Promise.

有了Promise,我们便可以将上面的读写操作,变成下面这样了:

//读
function readPath(path) {
    return new Promise((resolve,reject)=>{
        fs.readFile(path,'utf8',(error,data)=>{
            if(error) {
                reject(error);
            } else {
                resolve(data)
            }
        })
    })
}
//写
function wirtePath(path,data) {
    return new Promise((resolve,reject)=>{
        fs.writeFile(path,data,(error)=>{
            if(error) {
                reject(error)
            } else {
                resolve('success')
            }
        })
    })
}

readPath('./1.txt')
    .then((data)=>{
        wirtePath('./2.txt',data);
    },(reason)=>{
        console.log(reason);
    })
复制代码

更好的异步任务执行: 其基本思想为
用 async 标记的函数 代替生成器 , 用 await 代替 yield 来调用函数,就像这样

async function (){
    let content = await readPath('./1.txt');
    let msg = await wirtePath('./2.txt',content);
   console.log(msg)
}() // 这里是自执行函数
复制代码

手写Promise

我们觉得与其看各种各样的Promise面试题,都不如先实现一下Promise,明白了原理,自然更容易知道怎么用 现在,我们开始这篇文章的压轴代码,实现一个简单的Promise,

Promise的声明

首先,Promise肯定是一个类,我们就用 class 来声明

  • 由于 new Promise((resolve,reject) => {}),所以传入的参数是一个函数,我们叫他 executor ,传入就执行

  • executor 里面有两个参数 , 一个叫 resolve(成功),一个叫 reject(失败)

  • 由于 resolve 和 reject 可执行 , 所以都是函数 , 我们用let 声明

      class Promise {
          constructor(executor) {
              // 成功
              let resolve = (value) => {};
              // 失败
              let reject = (reason) => {};
          }
      }
    复制代码

解决基本状态

对Promise的规定

  • Promise存在三种状态 (state) pending ,fulfilled ,rejected

  • pending (等待状态)为初始状态 ,并可以转化为 fulfilled(成功状态) 和 rejected(失败状态)

  • 成功时 , 不可转为其他状态 , 并且必须有一个不可改变的值 (value)

  • 失败时 , 不可转为其他状态 , 并且必须有一个不可改变的原因(reason)

  • new Promise((resolve, reject)=>{resolve(value)}) resolve为成功,接收参数value,状态改变为fulfilled,不可再次改变。

  • new Promise((resolve, reject)=>{reject(reason)}) reject为失败,接收参数reason,状态改变为rejected,不可再次改变。

  • 若是executor函数报错 直接执行reject();

      class Promise {
          constructor(executor) {
              //初始化 state 为等待状态
              this.state = 'pending';
              // 成功的值
              this.value = undefined;
              // 失败的值
              this.reason = undefined;
              let resolve = (value) => {
                  // state 改变 , resolve 调用会失败
                  if(this.state === 'pending') {
                      this.state = 'fulfilled';// 调用 resolve后,state转化为成功状态
                      this.value = value;// 存储成功的值
                      
                  }
              };
              // 失败
              let reject = (reason) => {
                  //state 改变后, reject 调用就会失败
                  if(this.state === 'pending') {
                      // reject 调用后, state 转化为失败状态
                      this.state = 'rejected';
                      this.reason = reason;// 存储失败的原因
                  }
              };
              // 立即执行
              // 如果 executor 执行报错 , 直接执行 reject
              try {
                  executor(resolve,reject);
              } catch (err) {
                  reject(err);
              }
          }
      }
    复制代码

then方法

规定 : Promise有一个叫做then的方法 , 里面有两个参数 :onFulfilled , onRejected ,成功有成功的值 ,失败有失败的原因

  • 当状态state为 fulfilled ,则执行 onFulfilled , 传入this.value .当状态 state为 rejected , 则执行 onRejected , 传入this.reason

  • onFulfilled, onRejected 如果他们是函数, 则必须分别在 fulfilled , rejected 后被调用 , value 或 reason 依次作为他们的第一个参数

      then(onFulfilled,onRejected) {
          // 状态为 fulfilled , 执行 onFulfilled , 传入成功的值
          if(this.state === 'fulfilled') {
              onFulfilled(this.value);
          };
          // 状态为rejected , 执行onRejected , 传入失败的原因
          if(this.state === 'rejected') {
              onRejected(this.reason);
          }
      }
    复制代码

解决异步问题

现在基本可以实现简单的同步代码 ,但是当 resolve在setTimeout 内执行 , then 时state还是pending等待状态 , 我们就需要在 then调用的时候 , 将成功和失败存到各自的数组 , 一旦 reject 或者 resolve ,就调用他们。 类似于发布订阅 , 先将then里面的两个函数存储起来 , 由于一个 Promise可以有多个 then , 所以存在同一个数组内。

// 手写Promise
class Promise {
    constructor(executor) {
        //初始化状态
        this.state = 'pending';
        // 成功值
        this.value = undefined;
        // 失败值
        this.reason = undefined;
        // 成功存放的数组
        this.onResolvedCallbacks = [];
        // 失败存放的数组
        this.onRejectedCallbacks = [];
        // 成功
        let resolve = (value) => {
            // state 改变 , resolve 调用
            if(this.state === 'pending') {
                this.state = 'fulfilled';// 调用 resolve 后 , state转换为成功状态
                this.value = value;//存储成功的值
                // 一旦resolve 执行, 调用成功数组的函数
                this.onResolvedCallbacks.forEach(fn => fn());
            }
        }
        // 失败
        let reject = (reason) => {
            if(this.state === 'pending') {
                this.state = 'rejected';
                this.reason = reason;
                // 一旦 resolve执行 , 调用失败数组的函数
                this.onRejectedCallbacks.forEach(fn => fn());
            }
        }
        try {
            executor(resolve,reject);
        } catch (error) {
            reject(error)
        }
    }
    then(onFulfilled,onRejected) {
        // 状态为 fulfilled , 执行 onFulfiiled , 传入成功的值
        if(this.state === 'fulfilled') {
            onFulfilled(this.value);
        };
        //状态为 rejected , 执行onRejected , 传入失败的原因
        if(this.state === 'rejected') {
            onRejected(this.reason);
        }
        // 当状态为 pending时
        if(this.state === 'pending') {
            // onFulfilled 传入到成功数组中
            this.onResolvedCallbacks.push(()=>{
                onFulfilled(this.value);
            })
            // onRejected 传入到失败数组
            this.onRejectedCallbacks.push(()=>{
                onRejected(this.reason);
            })
        }
    }
    catch() {

    }
}
复制代码

解决链式调用

我们常常用到的 new Promise().then().then(),这就是链式调用,用来解决回调地狱的问题。
复制代码
  1. 为了达成链式 , 我们默认在第一个then里返回一个Promise. 规定 : 在then里面返回一个新的Promise , 称为promise2;
    promise2 = new Promise((resolve,reject)=>{})
  • 将这个promise2 返回的值传递到下一个then中
  • 如果返回一个普通的值 , 则将普通的值传递给下一个then中
  1. 当我们在第一个then中 return 了一个参数(参数未知,需要判断)。这个return 出来的新的promise就是 onFulfilled()或 onRejected()的值。 规定 : onFulfilled()或 onRejected()的值 , 即第一个then返回的值 , 叫做 x ,判断 x 的函数叫做 resolvePromise
  • 首先 , 要看 x 是不是 promise

  • 如果是promise , 则取它的结果 , 作为新的 promise2

  • 如果是普通值 , 直接作为 promise2 成功的结果

  • 所以要比较 x 和 promise2

  • resolvePromise 的参数有 promise2 (默认返回的 promise) , x(我们自己 return 的对象) , resolve , reject

  • resolve 和 reject 是promise2的

      class Promise {
          constructor(executor) {
              //初始化状态
              this.state = 'pending';
              // 成功值
              this.value = undefined;
              // 失败值
              this.reason = undefined;
              // 成功存放的数组
              this.onResolvedCallbacks = [];
              // 失败存放的数组
              this.onRejectedCallbacks = [];
              // 成功
              let resolve = (value) => {
                  // state 改变 , resolve 调用
                  if (this.state === 'pending') {
                      this.state = 'fulfilled';// 调用 resolve 后 , state转换为成功状态
                      this.value = value;//存储成功的值
                      // 一旦resolve 执行, 调用成功数组的函数
                      this.onResolvedCallbacks.forEach(fn => fn());
                  }
              }
              // 失败
              let reject = (reason) => {
                  if (this.state === 'pending') {
                      this.state = 'rejected';
                      this.reason = reason;
                      // 一旦 resolve执行 , 调用失败数组的函数
                      this.onRejectedCallbacks.forEach(fn => fn());
                  }
              }
              try {
                  executor(resolve, reject);
              } catch (error) {
                  reject(error)
              }
          }
          then(onFulfilled, onRejected) {
              // 声明返回的 promise2
              let promise2 = new Promise((resolve, reject) => {
                  // 状态为 fulfilled , 执行 onFulfiiled , 传入成功的值
                  if (this.state === 'fulfilled') {
                      let x = onFulfilled(this.value);
                      // resolvePromise 函数 , 处理自己 return 的 promise和 默认的 promise2的关系
                      resolvePromise(promise2, x, resolve, reject);
                  };
                  //状态为 rejected , 执行onRejected , 传入失败的原因
                  if (this.state === 'rejected') {
                      let x = onRejected(this.reason);
                      resolvePromise(promise2, x, resolve, reject);
                  }
                  // 当状态为 pending时
                  if (this.state === 'pending') {
                      // onFulfilled 传入到成功数组中
                      this.onResolvedCallbacks.push(() => {
                          let x = onFulfilled(this.value);
                          resolvePromise(promise2,x,resolve,reject);
                      })
                      // onRejected 传入到失败数组
                      this.onRejectedCallbacks.push(() => {
                          let x = onRejected(this.reason);
                          resolvePromise(promise2,x,resolve,reject);
                      })
                  }
              });
              // 返回promise2 , 完成链式
              return promise2;
          }
      }
    复制代码

完成 resolvePromise函数

规定 : 一段代码 , 让不同的promise代码互相套用 , 叫做resolvePromise

  • 如果 x === promise2 , 则是会造成循环引用 , 自己等待自己完成 ,则报 "循环引用" 错误。

      function resolvePromise(promise2,x,resolve,reject) {
          if(x === promise2) {
              return reject(new TypeError('Chaining cycle detected for promise'));
          }
      }
    复制代码
  1. 判断 x
  • 如果 x 是一个对象或者函数 , 则设为 x
  • x 不能是 null
  • x 是普通值 , 则直接 resolve(x)
  • x 是对象或者函数 (包括promise), let then = x.then;
  • 声明了 then
  • 如果取 then 报错 , 则走 reject()
  • 如果 then是个函数 , 则用 call执行then , 第一个参数是this ,后面是成功的回调和失败的回调
  • 如果是成功的回调还是promise ,就继续递归
  • 成功和失败只能调用一个 , 所以设定一个 called来防止多次调用。

note : 这个方法是 Promise 原码的精髓 , 可以仔细看看

    // 完成 resolvePromise函数
    function resolvePromise(promise2, x, resolve, reject) {
        // 循环引用报错
        if (x === promise2) {
            return reject(new TypeError('Chaining cycle detected for promise'));
        }
        //防止多次调用
        let called;
        // x不是null 并且 x是对象或者函数
        if (x != null && (typeof x === 'object' || typeof x === 'function')) {
            try {
                // A+ 规定 , 声明 then = x 的 then 方法
                let then = x.then;
                //如果 then 是函数 , 就默认是promise 了
                if (typeof then === 'function') {
                    // 就让 then 执行 第一个参数是 this 后面是成功的回调 和失败的回调
                    then.call(x, y => {
                        // 成功和失败只能调用一个
                        if (called) return;
                        called = true;
                        // resolve 就结果依旧是 promise 那就继续解析
                        resolvePromise(promise2, y, resolve, reject);
                    }, err => {
                        // 成功和失败只能调用一个
                        if (called) return;
                        called = true;
                        reject(err);
                    })
                } else {
                    resolve(x); // 直接成功即可
                }
            } catch (e) {
                // 也属于失败
                if(called) return;
                called = true;
                reject(e);
            }
        } else {
            resolve(x);
        }
    }
复制代码

其他问题

  1. 规定 : onFulfilled , onRejected 都是可选参数 , 如果他们不是函数 , 必须被忽略
  • onFulfilled返回一个普通的值 , 成功时直接等于 value => value
  • onRejected 返回一个普通的值 , 失败时如果直接等于 value => value, 则会跑到下一个 then中的 onFulfilled 中 , 所以直接扔出一个错误 err => throw err
  1. 规定 onFulfilled 或 onRejected 不能同步被调用 , 必须异步调用。我们就用 setTimeout解决异步问题
  2. 如果onFulfilled 或 onRejected报错 , 则直接返回 reject()

最终版本

可以 look a look

// 手写Promise
class Promise {
    constructor(executor) {
        // 初始化state 为等待状态
        this.state = 'pending';
        // 成功的值
        this.value = undefined;
        // 失败的值
        this.reason = undefined;
        // 成功存放的数组
        this.onResolvedCallbacks = [];
        // 失败存放的数组
        this.onRejectedCallbacks = [];
        // 成功
        let resolve = (value) => {
            // state 改变 , resolve 调用的会失败
            if (this.state === 'pending') {
                this.state = 'fulfilled'; // resolve调用后 , state转化为成功态
                this.value = value;// 存储成功的值
                // 一旦resolve执行 , 调用成功数组的函数
                this.onResolvedCallbacks.forEach(fn => fn());
            }
        }
        // 失败
        let reject = (reason) => {
            // state改变后 , reject调用就会失败
            if (this.state === 'pending') {
                // reject 调用后 , state 转换为失败状态
                this.state = 'rejected';
                this.reason = reason;// 存储失败的原因
                // 一旦reject 执行 , 调用失败数组的函数
                this.onRejectedCallbacks.forEach(fn => fn());
            }
        };
        // 立即执行
        // 如果 executor 执行报错 , 直接执行 reject
        try {
            executor(resolve, reject)
        } catch (err) {
            reject(err)
        }
    }
    then(onFulfilled, onRejected) {
        // onFulfilled 如果不是函数 , 就忽略 onFulfilled , 直接返回 value
        onFulfilled = typeof onFulfilled === "function" ? onFulfilled : value => value;
        // onRejected 如果不是函数 , 就忽略 onRejected , 直接扔出错误
        onRejected = typeof onRejected === "function" ? onRejected : err => { throw err };
        // 声明返回的 promise2
        let promise2 = new Promise((resolve, reject) => {
            if (this.state === 'fulfilled') {
                // 异步
                setTimeout(() => {
                    try {
                        let x = onFulfilled(this.value);
                        // resolvePromise函数 , 处理自己return 的promise和默认的promise2的关系
                        resolvePromise(promise2, x ,resolve,reject);
                    } catch (e) {
                        reject(e);
                    }
                },0)
            };
            if (this.state === 'rejected') {
                //异步
                setTimeout(() => {
                    // 如果报错
                    try {
                        let x = onRejected(this.reason);
                        resolvePromise(promise2,x,resolve,reject);
                    } catch (e) {
                        reject(e);
                    }
                },0);
            };
            // 当状态state为pending 时
            if (this.state === 'pending') {
                // onFulfilled传入到成功数组
                this.onResolvedCallbacks.push(() => {
                    //异步
                    setTimeout(() => {
                        try {
                            let x = onFulfilled(this.value);
                            resolvePromise(promise2,x , resolve,reject);
                        } catch (e) {
                            reject(e)
                        }
                    },0);
                })
                // onRejected 传入到失败数组
                this.onRejectedCallbacks.push(() => {
                    // 异步
                    setTimeout(() => {
                        try {
                            let x = onRejected(this.reason);
                            resolvePromise(promise2,x,resolve,reject);
                        } catch (e) {
                            reject(e)
                        }
                    })
                })
            }
        });
        // 返回promise , 完成链式
        return promise2;
    }
    catch(fn) {
        return this.then(null,fn);
    }
}


function resolvePromise(promise2, x, resolve, reject) {
    if (x === promise2) {
        return reject(new TypeError("Chaining cycle detected for promise"));
    }
    // 防止多次调用
    let called;
    // x 不是 null 并且 x 是对象或者函数
    if (x != null && (typeof x === 'object' || typeof x === 'function')) {
        try {
            // A+ 规定 , 声明 then = x 的then 方法
            let then = x.then;
            // 如果 then 是函数 , 就默认是 promise了
            if (typeof then === 'function') {
                // 就让 then 执行 第一个参数是 this 后面是成功的回调和 失败的回调
                then.call(x, y => {
                    // 成功和失败只能调用一个
                    if (called) return;
                    called = true;
                    // resolve 的结果依旧是 promise 那就继续解析
                    resolvePromise(promise2, y, resolve, reject);
                }, err => {
                    // 成功和失败只能调用一个
                    if (called) return;
                    called = true;
                    reject(err); // 失败了就失败了
                })
            } else {
                resolve(x); //直接成功即可
            }
        } catch (e) {
            // 也属于失败
            if (called) return;
            called = true;
            // 取then出错了 那就不要再继续执行了
            reject(e);
        }
    } else {
        resolve(x);
    }
}
复制代码

当然, 这里还有一些挂在 Promise上的方法

// resolve方法
Promise.resolve = function(val) {
    return new Promise((resolve,reject)=>{
      resolve(val)  
    })
}
// reject方法
Promise.reject = function(val) {
    return new Promise((resolve,reject)=> {
        reject(val);
    })
}
// all 方法 (获取所有的promise , 都执行then , 把结果放到数组 , 一起返回, 如果其中有一个发送了reject , 
则取出失败的时候最先被reject失败状态的值)
// 这个方法比较常用 , 一般用于 并发请求数据 ,ajax并发请求数据
Promise.all = function(promises) {
    let arr = [];
    let i = 0;
    functionData(index,data) {
        arr[index] = data;
        i++;
        if(i == promises.length) {
            resolve(arr);
        }
    }
    return new Promise((resolve,reject) => {
        for(let i = 0 ; i < promises.length ; i ++) {
            promises[i].then(data => {
                processData(i,data);
            },reject)
        }
    })
}
// race方法
// mdn的解释:
Promise.race()一旦可迭代的promise中的一个承诺实现或拒绝,该方法就会返回一个承诺,该承诺中包含承诺的价值或原因。
// 还是比较好理解的
Promise.race = function(promises) {
    return new Promise((resolve,reject) =>{
        for(let i = 0 ; i < promises.length ; i++) {
            promises[i].then(resolve,reject);
        }
    })
}

// 用法大概是下面这样了
    let p1 = new Promise((resolve, reject) => {
    setTimeout(() => {
      resolve('成功了')
    }, 1000);
  })
  
  let p2 = new Promise((resolve, reject) => {
    setTimeout(() => {
      resolve('success')    
    }, 900);
  })
  
  
  Promise.race([p1, p2]).then((result) => {
    console.log(result) // success
  })
复制代码

参考资料

  1. 《深入了解ES6》,电子工业出版社
  2. BAT前端经典面试问题:史上最最最详细的手写Promise教程

最后