一步步实现Promise代码封装

知乎文章地址:https://zhuanlan.zhihu.com/p/51373575

ES6中加入了Promise的一个概念,使得非阻塞式语句的同步变得简单起来,但是具体它是怎样实现的,接下来一步步地实现它。

一.Promise基本结构

new Promise((resolve, reject) => {
  setTimeout(() => {
    resolve('FULFILLED')
  }, 1000)
})

一个Promise实例通过上面的方式来实例化,它需要传入一个函数(我们称之为handle),handle又包含了resolve和reject两个参数,它们是两个函数,用来改变Promise对象的状态(这个我们之后再讲)。

因为Promise的构造函数只接受函数作为构造函数,否则就会报错,故而我们定义一个判断变量是否为函数的方法:

// 判断变量是否为函数
const isFunction = variable => typeof variable === 'function'

接下来,先将Promise的基本骨架先搭出来(我们以MyPromise作为Promise类名):

// 判断变量是否为函数
const isFunction = variable => typeof variable === 'function'

class MyPromise {
  constructor (handle) {
    if (!isFunction(handle)) {
      throw new Error('MyPromise must accept a function as a parameter')
    }
  }
}

二.Promise状态和值

Promise对象有3种状态:

  • Pending(进行中)
  • Fulfilled(已成功)
  • Rejected(已失败)

要注意的是,状态只能从pending到resolved或者从pending到rejected,当状态已经改变了,该Promise对象的状态就不会再改变了,并且之后当我们再对其进行.then操作(这个之后再看)时,.then中的回调函数会立即被执行。

另外,之前提到的handle函数,其可以传两个参数resolve和reject用于改变Promise对象的状态,并且传值给.then中的回调函数,这两个参数是Promise自身内部提供的,我们只需通过它们来调用即可,我们来看看resolve和reject的一些特性:

  1. resolve : 将Promise对象的状态从 Pending(进行中) 变为 Fulfilled(已成功)
  2. reject : 将Promise对象的状态从 Pending(进行中) 变为 Rejected(已失败)
  3. resolve 和 reject 都可以传入任意类型的值作为实参,表示 Promise 对象成功(Fulfilled)和失败(Rejected)的值

综上,我们可以知道我们需要设置一个状态属性(_status)用来存储状态,resolve与reject函数(_resolve和_reject)用来状态转换和传值,另外还有一个属性(_value 下文我们都叫做状态值)用来存储resolve或reject传递的值:

  • 首先定义三个常量,用于标记Promise对象的三种状态
// 定义Promise的三种状态
const PENDING = 'PENDING'
const FULFILLED = 'FULFILLED'
const REJECTED = 'REJECTED'
  • 添加状态属性和相应的状态值
// 添加状态
this._status = PENDING
// 添加状态值
this._value = undefined
  • 声明resolve与reject函数
// 添加resolve时执行的函数
_resolve(val) {
  if (this._status !== PENDING) return
  this._status = FULFILLED
  this._value = val
}
// 添加reject时执行的函数
_reject(err) {
  if (this._status !== PENDING) return
  this._status = REJECTED
  this._value = err
}
  • 在constructor中执行handle并调用resolve或者reject
// 执行handle
handle(this._resolve.bind(this), this._reject.bind(this))

另外,值得注意的是,对于Promise通过reject传递值其实和throw new Error等价,不过如果Promise状态已经变成Resolved,再抛出错误是无效的,故而我们可以这样改:

// 执行handle
try {
  handle(this._resolve.bind(this), this._reject.bind(this))
} catch (err) {
  this._reject(err)
}

这里其实用的很巧妙,实栗说话:

  1. 假如我们通过抛出异常的方式来转换状态并传递值,我们可以通过catch来捕获这个异常对象,并将其通过reject来传递;
  2. 假如handle中先做resolve处理,再进行抛出异常,这个时候,由于之前的resolve已经把Promise对象的状态转换为Fulfilled,故而再通过reject(err)其实是没有任何作用的,也就使得上面说的“状态已经变成Resolved,再抛出错误是无效的”的这种效果。

到了现在,这一块其实已经很全面了,不过还是有些缺陷的,就是resolve和reject函数应该是非阻塞式的,目前还是阻塞式的。

上面的代码可见demo1:

https://github.com/yaodebian/ES6_Study/blob/master/ES6_sourcecode_analyse/Promise_Code_By_Js/demo1.js​github.com

三.Promise的then方法

该方法使得异步的同步化变得简单,即.then中的回调函数必须等待Promise的状态发生改变再执行。

Promise对象的then方法接受两个参数(均可选):

promise.then(onFulfilled, onRejected)

传进的两个方法分别用于监听Promise对象状态转换为fulfilled和rejected时被执行,其返回一个Promise对象。(这里值得注意的是,如果.then传入的不是函数,则返回的Promise其传递的value值为前一个Promise传递的值)

另外,then方法可以被同一个Promise对象多次调用,里面的回调函数则按照注册的顺序执行,同时由于其返回一个Promise对象,故而可以链式调用,如:

promise1.then(onFulfilled1, onRejected1).then(onFulfilled2, onRejected2)

接下来针对onFulfilled和onRejected的几种情况进行分析:

  1. 如果 onFulfilled 或者 onRejected 返回一个值 x ,则运行下面的 Promise 解决过程:
  • 若x不为Promise,则使x作为.then方法返回的Promise对象的状态值并用于传递给下一个.then回调中,并且当前Promise对象的状态变为fulfilled;
  • 若x为Promise,这时当前.then返回的Promise对象就会等待x的状态发生变化,才会触发自身状态的变化,自身状态和状态值即为x的状态和状态值。

下面的栗子用于帮助理解:

let promise1 = new Promise((resolve, reject) => {
  setTimeout(() => {
    resolve()
  }, 1000)
})
promise2 = promise1.then(res => {
  // 返回一个普通值
  return '这里返回一个普通值'
})
promise2.then(res => {
  console.log(res) //1秒后打印出:这里返回一个普通值
})
let promise1 = new Promise((resolve, reject) => {
  setTimeout(() => {
    resolve()
  }, 1000)
})
promise2 = promise1.then(res => {
  // 返回一个Promise对象
  return new Promise((resolve, reject) => {
    setTimeout(() => {
     resolve('这里返回一个Promise')
    }, 2000)
  })
})
promise2.then(res => {
  console.log(res) //3秒后打印出:这里返回一个Promise
})

2.如果onFulfilled或者onRejected抛出一个异常e,则该.then方法返回的Promise对象的状态为rejected,状态值则为e。

let promise1 = new Promise((resolve, reject) => {
  setTimeout(() => {
    resolve('success')
  }, 1000)
})
promise2 = promise1.then(res => {
  throw new Error('这里抛出一个异常e')
})
promise2.then(res => {
  console.log(res)
}, err => {
  console.log(err) //1秒后打印出:这里抛出一个异常e
})

3.如果onFulfilled和onRejected不是函数,则.then对应的Promise对象其状态以及状态值和前一个Promise一样。

根据上面提到的,我们来进一步完善MyPromise

  • 修改constructor:增加执行队列

因为then方法能够被同一个Promise对象调用多次,我们维护两个数组,将每次then方法注册时的回调函数添加到数组中,等待执行:

constructor(handle) {
  if (!isFunction(handle)) {
    throw new Error('MyPromise must accept a function as a parameter')
  }
  // 添加状态
  this._status = PENDING
  // 添加状态值
  this._value = undefined
  // 添加成功回调函数队列
  this._fulfilledQueues = []
  // 添加失败回调函数队列
  this._rejectedQueues = []
  // 执行handle
  try {
    handle(this._resolve.bind(this), this._reject.bind(this))
  } catch (err) {
    this._reject(err)
  }
}
  • 添加then方法

对于then方法的执行,有两种情况:调用then的Promise对象的状态仍然为pending没有改变(将onFulfilled或onRejected添加进执行队列中,用于设置在前一个Promise对象状态改变时执行,这个接下来会讲到),以及状态已经改变(直接执行onFulfilled或onRejected)。

// 添加then方法
then(onFulfilled, onRejected) {
  const {
    _value,
    _status
  } = this
  switch (_status) {
    // 当状态为pending时,将then方法回调函数假如执行队列等待执行
    case PENDING:
      this._fulfilledQueues.push(onFulfilled)
      this._rejectedQueues.push(onRejected)
      break
      // 当状态已经改变时,立即执行对应的回调函数
    case FULFILLED:
      onFulfilled(_value)
      break
    case REJECTED:
      onRejected(_value)
      break
  }
  // 返回一个新的Promise对象
  return new MyPromise((onFulfilledNext, onRejectedNext) => {})
}

到目前,全部代码可见demo2:

https://github.com/yaodebian/ES6_Study/blob/master/ES6_sourcecode_analyse/Promise_Code_By_Js/demo2.js​github.com

  • 接下来,针对.then通过return和抛出异常来改变状态,我们做如下处理:

首先我们在返回的Promise对象的回调中声明一个方法,该方法用于在前一个Promise对象状态改变为Fulfilled时执行,并设置当前Promise对象的状态值。

// 封装一个成功时执行的函数
let fulfilled = value => {
  try {
    // 如果不是函数,直接将前一个Promise的状态和状态值赋给当前Promise
    if (!isFunction(onFulfilled)) {
      onFulfilledNext(value)
    } else {
      // 先执行回调函数的内容
      let res = onFulfilled(value);
      // 如果返回值是一个Promise对象,将该Promise对象的状态和状态值赋给当前Promise对象
      if (res instanceof MyPromise) {
        // 如果当前回调函数返回MyPromise对象,必须等待其状态改变后在执行下一个回调
        res.then(onFulfilledNext, onRejectedNext)
      // 如果返回值为非Promise对象,则将当前Promise对象的状态转换为已完成,并将返回值当作传递值传递给当前Promise对象的状态值
      } else {
        //否则会将返回结果直接作为参数,传入下一个then的回调函数,并立即执行下一个then的回调函数
        onFulfilledNext(res)
      }
    }
  // 用于捕获异常,其实也就是reject的传递值
  } catch (err) {
    // 如果函数执行出错,新的Promise对象的状态为失败
    onRejectedNext(err)
  }
}

同理,封装一个Rejected时执行的方法:

// 封装一个失败时执行的函数
let rejected = error => {
  try {
    if (!isFunction(onRejected)) {
      onRejectedNext(error)
    } else {
      let res = onRejected(error);
      if (res instanceof MyPromise) {
        // 如果当前回调函数返回MyPromise对象,必须等待其状态改变后在执行下一个回调
        res.then(onFulfilledNext, onRejectedNext)
      } else {
        //否则会将返回结果直接作为参数,传入下一个then的回调函数,并立即执行下一个then的回调函数
        onFulfilledNext(res)
      }
    }
  } catch (err) {
    // 如果函数执行出错,新的Promise对象的状态为失败
    onRejectedNext(err)
  }
}

最后我们将之前的switch放进返回的Promise里面,代码详见:

https://github.com/yaodebian/ES6_Study/blob/master/ES6_sourcecode_analyse/Promise_Code_By_Js/then_code.js​github.com

  • 对_resolve和_reject进行修改:通过前一个Promise对象的resolve和reject对.then中当前的Promise对象的状态和状态值进行改变。
// 添加resovle时执行的函数
_resolve(val) {
  // 状态只会改变一次,改变之后便不能更改,并且之后的每次调用均无效
  if (this._status !== PENDING) return
  // 依次执行成功队列中的函数,并清空队列
  const run = () => {
    this._status = FULFILLED
    this._value = val
    let cb;
    while (cb = this._fulfilledQueues.shift()) {
      cb(val)
    }
  }
  // 为了支持同步的Promise,这里采用异步调用
  setTimeout(() => run(), 0)
}
// 添加reject时执行的函数
_reject(err) {
  if (this._status !== PENDING) return
  // 依次执行失败队列中的函数,并清空队列
  const run = () => {
    this._status = REJECTED
    this._value = err
    let cb;
    while (cb = this._rejectedQueues.shift()) {
      cb(err)
    }
  }
  // 为了支持同步的Promise,这里采用异步调用
  setTimeout(run, 0)
}

到目前为止,Promise的基本功能基本上已经出来了,代码可见demo3:

https://github.com/yaodebian/ES6_Study/blob/master/ES6_sourcecode_analyse/Promise_Code_By_Js/demo3.js​github.com

接下来,我们来测试一下之前写好的代码:

// .then调用:Promise状态转换为fulfilled
new MyPromise((resolve, reject) => {
  resolve('hahaha')
})
.then((val) => {
  console.log(val) // haha
})
// .then调用:Promise状态转换为rejected
new MyPromise((resolve, reject) => {
  reject('hahaha')
})
.then(null, (err) => {
  console.log(err) // haha
})
// .then调用:Promise状态转换为rejected (通过抛出异常来做)
new MyPromise((resolve, reject) => {
  throw new Error('yaodebian')
})
.then(null, (err) => {
  console.log(err) 
  /*
  Error: yaodebian
    at MyPromise (C:\Users\qi\Desktop\jsTest\Promise\index3.js:145:9)
    at new MyPromise (C:\Users\qi\Desktop\jsTest\Promise\index3.js:24:7)
    at Object.<anonymous> (C:\Users\qi\Desktop\jsTest\Promise\index3.js:144:1)
    at Module._compile (module.js:635:30)
    at Object.Module._extensions..js (module.js:646:10)
    at Module.load (module.js:554:32)
    at tryModuleLoad (module.js:497:12)
    at Function.Module._load (module.js:489:3)
    at Function.Module.runMain (module.js:676:10)
    at startup (bootstrap_node.js:187:16)
  */ 
})
// .then调用:参数返回一个非Promise的变量,状态均变为fulfilled
new MyPromise((resolve, reject) => {
  resolve('haha')
})
.then((val) => 'yaodebian')
.then((val) => console.log(val)) // yaodebian

new MyPromise((resolve, reject) => {
  reject('haha')
})
.then(null, (val) => 'yaodebian')
.then((val) => console.log(val)) // yaodebian
// .then调用:参数返回一个Promise的变量,状态与状态值按照返回的Promise而定
new MyPromise((resolve, reject) => resolve('haha'))
.then((val) => new MyPromise((resolve, reject) => setTimeout(() => resolve('yaodebian'), 2000)))
.then((val) => console.log(val)) // 1秒后打印yaodebian

new MyPromise((resolve, reject) => reject('haha'))
.then(null, (val) => new MyPromise((resolve, reject) => setTimeout(() => resolve('yaodebian'), 2000)))
.then((val) => console.log(val)) // 1秒后打印yaodebian

new MyPromise((resolve, reject) => resolve('haha'))
.then((val) => new MyPromise((resolve, reject) => setTimeout(() => reject('yaodebian'), 2000)))
.then(null, (err) => console.log(err)) // 1秒后打印yaodebian

new MyPromise((resolve, reject) => reject('haha'))
.then(null, (val) => new MyPromise((resolve, reject) => setTimeout(() => reject('yaodebian'), 2000)))
.then(null, (err) => console.log(err)) // 1秒后打印yaodebian
// .then调用:抛出一个异常对象,状态变为rejected
new MyPromise((resolve, reject) => {
  resolve('haha')
})
.then((val) => {
  throw new Error('yaodebian')
})
.then(null, (val) => console.log(val)) 
/*
Error: yaodebian
    at MyPromise.then (C:\Users\qi\Desktop\jsTest\Promise\index3.js:199:9)
    at fulfilled (C:\Users\qi\Desktop\jsTest\Promise\index3.js:73:23)
    at run (C:\Users\qi\Desktop\jsTest\Promise\index3.js:38:9)
    at Timeout.setTimeout [as _onTimeout] (C:\Users\qi\Desktop\jsTest\Promise\index3.js:42:22)
    at ontimeout (timers.js:475:11)
    at tryOnTimeout (timers.js:310:5)
    at Timer.listOnTimeout (timers.js:270:5) 
*/

new MyPromise((resolve, reject) => {
  reject('haha')
})
.then(null, (val) => {
  throw new Error('yaodebian')
})
.then(null, (val) => console.log(val))
/*
Error: yaodebian
    at MyPromise.then (C:\Users\qi\Desktop\jsTest\Promise\index3.js:207:9)
    at rejected (C:\Users\qi\Desktop\jsTest\Promise\index3.js:93:23)
    at Timeout.run [as _onTimeout] (C:\Users\qi\Desktop\jsTest\Promise\index3.js:53:9)
    at ontimeout (timers.js:475:11)
    at tryOnTimeout (timers.js:310:5)
    at Timer.listOnTimeout (timers.js:270:5)
*/
// .then调用:当对应的回调参数为空或者非函数,则继承前一个Promise的状态和状态值
new MyPromise((resolve, reject) => resolve('yaodebian'))
.then(null)
.then((val) => console.log(val)) // yaodebian

new MyProomise((resolve, reject) => reject('yaodebian'))
.then(null, null)
.then(null, (err) => console.log(err)) // yaodebian
// 同一个Promise多次调用.then
let p = new MyPromise((resolve, reject) => resolve('yaodebian'))
p.then(() => console.log(1))
p.then(() => console.log(2))
p.then(() => console.log(3))
p.then(() => console.log(4))
// 1 2 3 4
// Promise状态改变后便不能改变
let p = new MyPromise((resolve, reject) => setTimeout(() => resolve('yaodebian'), 1000))
setTimeout(() => p.then(() => console.log(1)), 0) // 到执行时还要等1秒才会执行
setTimeout(() => p.then(() => console.log(2)), 2000) // 2秒时立即执行
setTimeout(() => p.then(() => console.log(3)), 3000) // 3秒时立即执行
setTimeout(() => p.then(() => console.log(4)), 4000) // 4秒时立即执行

另外,还有几种情况是我们没有考虑到的:

  1. 如果一个Promise其resolve传递的值(注意仅限resolve)如果是另一个Promise对象,那么它需要等待这个Promise对象的状态发生改变,其状态才会发生改变,并且它的状态和状态值是这个Promimse对象的状态与状态值,针对这种情况,我们对_resolve进行些许的修改。
// 添加resovle时执行的函数
_resolve (val) {
  const run = () => {
    if (this._status !== PENDING) return
    this._status = FULFILLED
    // 依次执行成功队列中的函数,并清空队列
    const runFulfilled = (value) => {
      let cb;
      while (cb = this._fulfilledQueues.shift()) {
        cb(value)
      }
    }
    // 依次执行失败队列中的函数,并清空队列
    const runRejected = (error) => {
      let cb;
      while (cb = this._rejectedQueues.shift()) {
        cb(error)
      }
    }
    /* 
      如果resolve的参数为Promise对象,则必须等待该Promise对象状态改变后,
      当前Promsie的状态才会改变,且状态取决于参数Promsie对象的状态
    */
    if (val instanceof MyPromise) {
      val.then(value => {
        this._value = value
        runFulfilled(value)
      }, err => {
        this._value = err
        runRejected(err)
      })
    } else {
      this._value = val
      runFulfilled(val)
    }
  }
  // 为了支持同步的Promise,这里采用异步调用
  setTimeout(run, 0)
}

2.我们发现,某个Promise对象的状态变为rejected,这种情况如果没有得到处理,会发生报错(chrome报错、nodejs则是发出警告),举个栗子:

// reject状态没有处理则报错
new Promise((resolve, reject) => reject('yaodebian'))
/*
(node:6324) UnhandledPromiseRejectionWarning: Unhandled promise rejection (rejection id: 1): yaodebian
(node:6324) [DEP0018] DeprecationWarning: Unhandled promise rejections are deprecated. In the future, promise rejections that are not handled will terminate the Ned will terminate the Node.js process with a non-zero exit code.
*/

上面 的第二句警告,在谷歌翻译是这样的:未处理的承诺拒绝已被弃用。 将来,未处理的承诺拒绝将终止,Ned将使用非零退出代码终止Node.js进程。

所以这时我们希望如果rejected的状态如果没有被后序的.then进行处理,我们则给出一个警告,这个其实很简单,我们只要简单修改_reject即可,如下:

  // 添加reject时执行的函数
  _reject(err) {
    if (this._status !== PENDING) return
    // 依次执行失败队列中的函数,并清空队列
    const run = () => {
      this._status = REJECTED
      this._value = err
      if (this._rejectedQueues.length === 0) {
        throw new Error(this._value)
      }
      let cb;
      while (cb = this._rejectedQueues.shift()) {
        cb(err)
      }
    }
  }

上述我们通过判断this._rejectedQueues的length是否为0即可,如果为0,说明没有对该状态的处理,我们则抛出一个异常。(这里其实用的很巧妙,只要一个Promise对象没有.then处理,并且其状态为rejected,则会抛出异常)

// reject状态没有处理则报错
new MyPromise((resolve, reject) => reject('yaodebian'))
/*
 throw new Error(this._value)
        ^

Error: yaodebian
    at Timeout.run [as _onTimeout] (C:\Users\qi\Desktop\jsTest\Promise\index3.js:88:15)
    at ontimeout (timers.js:475:11)
    at tryOnTimeout (timers.js:310:5)
    at Timer.listOnTimeout (timers.js:270:5)
*/

到了目前,Promise的实现基本已经结束。

代码详见demo4:

https://github.com/yaodebian/ES6_Study/blob/master/ES6_sourcecode_analyse/Promise_Code_By_Js/demo4.js​github.com

四. .catch方法

该API其实就是.then(null, onRejected)的一个缩写,所以我们可以直接这样添加:

// 添加catch方法
catch (onRejected) {
  return this.then(null, onRejected)
}

五. Promise.resolve方法

有时需要将现有对象转为Promise对象,Promise.resolve方法就起到这个作用。

  • 参数是一个Promise实例:直接返回这个Promise对象;
  • 参数是一个thenable对象:将这个对象转换为Promise对象,然后立即执行thenable对象的then方法,then是一个具有resolve和reject两个参数的函数方法;

thenable对象的格式与下面相仿:

{
  then: function(resolve, reject) {
    resolve(42);
  }
}
  • 参数不是then方法的对象,或根本就不是对象:Promise.resolve方法会返回一个Promise对象,状态通过resolve转为resolved,并将该对象的值进行传递;
  • 不带有任何参数:直接返回一个resolved状态的Promise对象,如果我们希望得到一个Promise对象,比较方便的方法是直接调用Promise.resolve方法。
  • 不要以为如果我传递的参数是一个Error类型就会使得Promise对象状态转为rejected,它的状态还是resolved;

根据上面的一些规则,我们添加resolve静态方法:

// 添加静态resolve方法
static resolve (value) {
  // 如果参数是MyPromise实例,直接返回这个实例
  if (value instanceof MyPromise) return value
  // 如果参数是thenable对象,则返回一个Promise对象,并且其回调为thenable对象中的then属性
  if (value && value.then && isFunction(value.then)) return new MyPromise(value.then)
  // 其他情况
  return new MyPromise(resolve => resolve(value))
}

六.Promise.reject方法

该静态方法和resolve几乎相同:

  • 对于非thenable对象、非Promise对象的参数,返回一个Promise对象,其状态为rejected,且状态值为传入的参数;
  • 与resolve一样,可以不传入任何参数,生成一个状态为rejected的Promise对象;

但也有很多不同的地方法:

  • 即使传入的是thenable对象或者Promise对象值,状态不受影响 ,仍为rejected,并且状态值为传入的参数;

综上,静态reject方法就是将传入的参数当作状态值,并且状态为rejected。

接下来,添加该方法:

// 添加静态reject方法
static reject (err) {
  return new MyPromise((resolve, reject) => reject(err))
}

七.Promise.all方法

该api返回一个Promise实例p,传递一个数组作为参数,对于其中的不是Promise对象的项,会先通过Promise.resolve进行处理。

当数组中的每一项它的状态都变成fulfilled,返回的Promise实例才会将状态转为fulfilled,此时每一项的返回值组成一个数组,传递给p的回调函数;

当其中一项的状态转为rejected,p的状态就会变为rejected(第一个被reject的实例返回值时),并且将第一个被reject的实例的返回值传递给p的回调函数。

// 添加静态all方法
static all(list) {
  return new MyPromise((resolve, reject) => {
    // 返回值的集合
    let values = []
    let count = 0
    // 兼容String类型
    typeof list === 'string' ? list = list.split('') : ''
    for (let [i, p] of list.entries()) {
      // 数组参数如果不是MyPromise实例,先调用MyPromise.resolve
      this.resolve(p).then(res => {
        values[i] = res
        count++
        // 所有状态都变成fulfilled时返回的MyPromise状态就变成fulfilled
        if (count === list.length) resolve(values)
      }, err => {
        // 有一个被rejected时返回的MyPromise状态就变成rejected
        reject(err)
      })
    }
  })
}

由于all方法对于传入的参数不能枚举就会报错,故而,也就不用做其他处理。

八.Promise.race方法

race方法其实和all方法很像,不同的是,只要传入的可枚举参数的一项率先改变状态,其返回的Promise对象的状态就会改变,并以改变的那一项的状态和值作为Promise的状态和值。

// 添加race方法
static race(list) {
  return new MyPromise((resolve, reject) => {
    // 兼容String类型
    typeof list === 'string' ? list = list.split('') : ''
    for (let p of list) {
      // 只要有一个实例率先改变状态,新的MyPromise的状态就跟着改变
      this.resolve(p).then(res => {
        resolve(res)
      }, err => {
        reject(err)
      })
    }
  })
}

九.Promise辅助方法

Promise思想中有两个方法:done和finally,这两个方法并没有在Promise中进行设置,不过我们可以自己进行部署。

Done: 它用来在整个Promise回调链的尾端调用,保证抛出任何可能出现的错误。

它的实现代码还是相当简单的:

Promise.prototype.done = function (onFulfilled, onRejected) {
  this.then(onFulfilled, onRejected)
    .catch(function (reason) {
      // 抛出一个全局错误
      setTimeout(() => {
        throw reason
      }, 0);
    });
};

这里我们直接添加进MyPromise中:

// 添加done方法
done(onFulfilled, onRejected) {
  this.then(onFulfilled, onRejected)
    .catch(function (reason) {
      // 抛出一个全局错误
      setTimeout(() => {
        throw reason
      }, 0)
    })
}

finally: 用来指定不管Promise对象最后状态如何都会执行的操作,它接受一个普通的回调函数作为参数,该函数不管怎样都必须执行。

下面是一个例子,服务器使用Promise处理请求,然后使用 finally 方法关掉服务器。

server.listen(0)
.then(function () {
   // run test
})
.finally(server.stop);

它的实现也很简单。

Promise.prototype.finally = function (callback) {
  let P = this.constructor;
  return this.then(value => P.resolve(callback()).then(() => value),
    reason => P.resolve(callback()).then(() => { throw reason })
  );
};

这里我们同样添加进MyPromise中:

// 添加finally方法
finally(callback) {
  let P = this.constructor
  return this.then(value => P.resolve(callback()).then(() => value),
    reason => P.resolve(callback()).then(() => {
      throw reason
    })
  )
}

上面代码中,不管前面的Promise是 fulfilled 还是 rejected ,都会执行回调函数 callback 。

完整代码详见:

https://github.com/yaodebian/ES6_Study/blob/master/ES6_sourcecode_analyse/Promise_Code_By_Js/demo5.js​github.com

上述文章参考:

http://es6.ruanyifeng.com/#docs/promise​es6.ruanyifeng.com

Promise实现原理(附源码)​www.jianshu.com

发布了80 篇原创文章 · 获赞 91 · 访问量 15万+

猜你喜欢

转载自blog.csdn.net/YaoDeBiAn/article/details/84779054
今日推荐