Promise的秘密(Promise原理解析以及实现)

写在前面

本篇文章将会带大家从分解promise入手,一步步实现一个promise。但阅读之前需要比较熟练地了解了解用法,结合用法看文章可能更容易理解。

结构

先看一下简单的用法。

const promise = new Promise((resolve, reject) => {
 setTimeout(() => {
 resolve('success')
 })
})
.then(value => { ... }, reason => { ... })
.catch(error => { ... })
复制代码

Promise的构造函数接收了一个回调,这个回调就是下面要讲到的执行器,执行器的参数resolve, reject也是两个函数, 负责改变promise实例的状态和它的值,then函数中的回调在状态改变后执行。

注意:不是then函数在状态改变后执行,而是then中的回调函数在状态改变后执行。then方法会将其中的回调放入执行队列,promise的状态改变后再将队列中的函数一一执行。

如果要实现一个最简单的promise类,内部结构都要包含什么呢?

状态:fulfiled、rejected、pending。
值:promise的值。
执行器:提供改变promise状态的入口。
resolve和reject方法:前者将promise改变为fulfiled,后者将其改变为rejected。可以在执行器内根据实际业务来控制是resolve或reject。
then方法:接收两个回调,onFulfilled, onRejected。分别在promise状态变为fulfiled或rejected后执行,这里涉及到将回调注册进两个执行队列的操作,后文会讲到。

const PENDING = 'pending'
const FULFILLED = 'fulfiled'
const REJECTED = 'rejected'
class NewPromise {
 constructor(handler) {
 this.state = PENDING
 this.value = undefined
 this.successCallback = []
 this.failureCallback = []
 try {
 handler(this.resolve.bind(this), this.reject.bind(this))
 } catch (e) {
 this.reject(e)
 }
 }
 // resolve和reject方法
 resolve(value) { ... }
 reject(reason) { ... }
 // then方法
 then(onFulfilled, onRejected) { ... }
}
复制代码

结构中的每个部分是如何实现的呢?

Promise的执行器

执行器是我们初始化promise时候传入的回调,是我们操作promise的入口,所以执行器的实现不复杂,也就是将我们传入的回调执行一下。

class NewPromise {
 ...
 handler(resolve.bind(this), reject.bind(this))
 ...
}
复制代码

实际上,执行器会接受两个回调,resolve和reject。它们真正起到改变promise状态的作用。

resolve和reject

实际上是两个函数,作为参数传入到执行器中,所做的事情不复杂:

改变promise的状态
将接收的值作为promise的value
依次执行then中注册的回调
const PENDING = 'pending'
const FULFILLED = 'fulfiled'
const REJECTED = 'rejected'
class NewPromise {
 constructor(handler) {
 this.state = PENDING
 this.value = undefined
 // 两个队列,后面会讲到
 this.successCallback = []
 this.failureCallback = []
 try {
 // 执行器,由于resolve和reject中用到了this,这里需要bind一下
 handler(this.resolve.bind(this), this.reject.bind(this))
 } catch (e) {
 this.reject(e)
 }
 }
 resolve(value) {
 if (this.state !== PENDING) return
 this.state = FULFILLED
 this.value = value
 // 用setTimeout模拟异步方式
 setTimeout(() => {
 this.successCallback.forEach(item => {
 item(value)
 })
 })
 }
 reject(reason) {
 if (this.state !== PENDING) return
 this.state = REJECTED
 this.value = reason
 setTimeout(() => {
 this.failureCallback.forEach(item => {
 setTimeout(item(reason))
 })
 })
 }
}
复制代码

看一下它们的实现,改变状态、赋值value。最重要的一点:循环执行then方法注册到队列中的回调。 而规范中要求回调以异步方式执行,这样保证在执行所有的回调之前,所有回调已经通过then注册完成,所以这里用setTimeout模拟了一下。

then方法:

(翻译整理自Promise/A+ 规范)

promise必须提供then方法来访问这个promise当前或者最终的值。 then方法有两个参数:onFulfilled, onRejected,都是可选的。关于这两个参数,这里有几个规则:

onFulfilled, onRejected都不是函数的时候,必须被忽略

实际上忽略的意思也就是如果不是函数,默认给它赋值成函数,返回值为then所属的promise的值。这样是做是为了在then()函数未传回调的时候,可以将promise的值传递下去。场景如下:

promise(resolve => resolve('success'))
 .then()
 .then(function(value) {
 console.log(value)
 })
复制代码

具体实现上,在它不是函数的时候可以给它赋值一个默认函数,也可以直接调用新返回的promise中的resolve或reject将值传下去,来达到忽略的效果。

onFulfilled是函数的时候

必须在当前的promise的状态变为fulfilled的时候被调用,promise被resolve的值也就是它的第一个参数
不能在fulfilled之前被调用
最多只能被调用一次
onRejected是函数的时候

必须在当前的promise的状态变为rejected的时候被调用,promise被reject的值也就是它的第一个参数。不能在rejected之前被调用,
最多只能被调用一次。
then可能会被调用多次

当promise状态变为fulfilled,所有onFulfilled将会按照最开始在then方法中注册的顺序去调用
当promise状态变为rejected,所有onRejected将会按照最开始在then方法中注册的顺序去调用,就像下边这样:

 const promise = new Promise((resolve, reject) => {
 setTimeout(() => resolve('success'))
 })
 promise.then(res => {
 console.log(res, '第一次');
 })
 promise.then(res => {
 console.log(res, '第二次');
 })
复制代码

鉴于这

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值