手拉手,一步一步带你实现promise原理
前言
- 就在半个月前,一个后端的同事问我,为啥网上promise的实现原理里面有setTimeout呢?
- 我说我没了解过,但过后还是抱着好奇去了解promise的实现原理,然后。。。就有了下面这篇文章
- 等到我搞懂promise的实现原理之后,准备回答他有关setTimeout这个问题的时候,他说:哦!我上次说错了,我说的是axios的实现原理,啊这。。。。。。 废话不多说,直接进入正题
文件准备
-
建立一个文件夹 里面包含三个文件 promise.js index.js test.js
-
promise.js 用于手写我们的Promise
-
index.js 用于使用标准Promise ,验证对比我们写的Promise
-
test.js 用于测试我们写的Promise 同样代码需要分别放在test.js index.js 执行
-
如下图
-
说明:运行index.js 或者 test.js 全部手动用 node index.js 或者 node test.js 闲麻烦可以用nodemon,自行百度
Promises/A+规范
- Promise 对象是异步编程的一种解决方案,最早由社区提出。Promises/A+ 规范是 JavaScript Promise 的标准,规定了一个 Promise 所必须具有的特性
- Promises/A+规范文章 promisesaplus.com/ 能不能打开全靠手气
- 我根据上面Promises/A+规范文章,翻译并总结成 规范1 规范2 规范3 (具体见下文)
正片开始
Promise用法
-
日常开发,Promise主要是用来处理异步编程的
-
如何使用,看下面代码,在index.js中运行(node index.js)
const promise1 = new Promise((resolve,reject)=>{ console.log(1) resolve('解决') // reject('拒绝') }); promise1.then(value1=>{console.log('value1',value1)},reason1=>{console.log('reson1',reason1)}) // 输出 1 value1 解决 // reson1 拒绝 复制代码
分析
-
先看看 规范1
规范1: 1 promise 有三种状态,分别是 待定(pending) 已完成(fulfilled) 已拒绝(rejected) 1.1 待定(pending): 可能转换为 已完成(fulfilled) 或者 已拒绝(rejected) 1.2 已完成(fulfilled):必须由待定(pending)转换过来,不可变化,必须有一个不能改变的值来表示,我们称为终值value 1.3 已拒绝(rejected):必须由待定(pending)转换过来,不可变化,必须有一个不能改变的值表示,我们称为拒因reason 复制代码
-
再看new Promise,说明 Promise是个构造函数 ,new Promise 传递的是一个函数(取名为executor)。
-
并且在Promise类内部,executor是立即执行的。
代码思路
-
我们需要定义一个构造函数Promise
-
构造函数里面有一个表示当前状态的值state
-
构造函数里面有一个表示Promise已完成(fulfilled)的终值value
-
构造函数里面有一个表示Promise已拒绝(rejected)的拒因reason
-
并且立即执行executor函数,于是在promise.js文件中写入:
// promise.js class Promise { constructor(executor) { this.state = 'pending'; this.value = null; this.reason = null; executor() } } module.exports = Promise; 复制代码
-
同样在test.js文件中写入
// test.js const Promise = require('./promise') const promise = new Promise((resolve,reject)=>{ console.log(1) }) // 输出 1 复制代码
-
为了防止不必要的失误,我提醒一下:**const Promise = require('./promise') **这个代码永远存在在test.js文件中,且不被注释
-
接下来,我们接着思考:
思考
-
先看下面代码(可以在index.js运行):
const promise = new Promise((resolve,reject)=>{ console.log(1) resolve(2) }) promise.then(value => { console.log('value',value) }) // 输出 1 2 复制代码
分析
-
在promise.js文件中,executor() 没有传入参数
-
由上面代码中resolve(2) 可知:resolve和reject是函数,并且由构造函数提供
-
我们知道(强行你们知道):
-
resolve 方法的作用是:将待定状态(pending)转化为已完成状态(fulfilled) ,并且把resolve(value)中的value值,赋值给构造函数Promise中的value属性,并且转换之前的状态必须是pending状态。
-
reject方法的作用是:将待定状态(pending)转化为已拒绝状态(rejected) ,并且把reject(reason)中的reason值,赋值给构造函数Promise中的reason属性,并且转换之前的状态必须是pending状态。
-
-
分析之后我们可以更新promise.js文件:
更新promise.js代码如下
class Promise {
constructor(executor) {
this.state = 'pending';
this.value = null;
this.reason = null;
// 两方法传递给executor并且立即执行
executor(this.resolve,this.reject)
}
// 往 Promise类中 添加 resolve方法
resolve(value) {
if(this.state === 'pending') {
this.state = 'fulfilled';
this.value = value;
}
}
// 往 Promise类中 添加 reject方法
reject(reason) {
if(this.state === 'pending') {
this.state = 'rejected';
this.reason = reason;
}
}
}
module.exports = Promise;
复制代码
注意
- 因为函数resolve和reject是在构造函数Promise外边执行的,所以函数resolve和reject的this指向不是指向Promise的实例(指向undefined),我们要用bind绑定this。
- 还有当前Promise构造函数中有三个常量 'pending' 'fulfilled' 'rejected'。我们可以在Promise类上定义三个变量来存储,防止我们写代码拼写错误,减少不必要的bug。
- 稍微优化一下promise.js代码
更新promise.js代码如下
class Promise {
constructor(executor) {
// 初始化 状态 终值 拒因
this.initValue()
// 改变 resolve reject函数的this 指向
this.initBind()
// 立即执行 executor
executor(this.resolve,this.reject)
}
initValue() {
this.state = Promise.PENDING;
this.value = null;
this.reason = null;
}
initBind() {
this.resolve = this.resolve.bind(this)
this.reject = this.reject.bind(this)
}
resolve(value) {
if(this.state === Promise.PENDING) {
this.state = Promise.FULFILLED;
this.value = value;
}
}
reject(reason) {
if(this.state === Promise.PENDING) {
this.state = Promise.REJECTED;
this.reason = reason;
}
}
}
// 定义三个常量
Promise.PENDING = 'pending';
Promise.FULFILLED = 'fulfilled';
Promise.REJECTED = 'rejected';
module.exports = Promise;
复制代码
这里有一个注意点
- 当executor 不是一个函数的时候,比如我们可以在index.js 运行如下代码:
new Promise(1)
// 结果 TypeError: Promise resolver 1 is not a function
复制代码
- 因此我们应该对executor加多一个判断
更新promise.js函数----对executor判断
class Promise {
constructor(executor) {
// 对executor类型判断 不是函数 直接抛出错误
if(typeof executor !== 'function') {
throw new TypeError(`Promise resolver ${executor} is not a function`)
}
this.initValue()
this.initBind()
executor(this.resolve,this.reject)
}
initValue() {
this.state = Promise.PENDING;
this.value = null;
this.reason = null;
}
initBind() {
this.resolve = this.resolve.bind(this)
this.reject = this.reject.bind(this)
}
resolve(value) {
if(this.state === Promise.PENDING) {
this.state = Promise.FULFILLED;
this.value = value;
}
}
reject(reason) {
if(this.state === Promise.PENDING) {
this.state = Promise.REJECTED;
this.reason = reason;
}
}
}
Promise.PENDING = 'pending';
Promise.FULFILLED = 'fulfilled';
Promise.REJECTED = 'rejected';
module.exports = Promise;
复制代码
- 到此为止,Promise的 规范1 实现了,是不是很简单啊,promise?就这?好戏在后头(手动狗头)
then方法
- 我们接下来讲规范2,让我娓娓道来。
规范2:
2 then方法
用法:promise.then(onFulfilled, onRejected)
2.1 这两个onFulfilled和onRejected都是可选的参数
2.1.1 如果onFulfilled不是函数,则必须忽略它 => 其实不是忽略,是将他重新赋值一个函数 下文会讲到
2.1.2 如果onRejected不是函数,则必须忽略它 => 其实不是忽略,是将他重新赋值一个函数 下文会讲到
2.2 如果onFulfilled是一个函数
2.2.1 必须在promise完成(由pending到fulfilled,即调用resolve(value))后才调用它,并将promise的值(终值 value)作为它的第一个参数
2.2.2 在promise完成之前不能调用它
2.3 如果onRejected是一个函数,
2.3.1 必须在promise被拒绝(由pending到rejected,即调用reject(reason)后才调用它,并将promise的(拒因reason)作 为它的第一个参数
2.3.2 在promise被拒绝之前不能调用它
2.4 then 必须返回一个promise实例,这样才可以实现链式调用,后文会讲
我们看看下面例子:
eg: promise2 = promise1.then(onFulfilled, onRejected);
2.4.1 如果onFulfilled或onRejected的返回值为x,则运行函数resolvePromise(promise2, x)
// 说明: 函数resolvePromise是我们自己定义的,后文会讲到
2.4.2 如果onFulfilled或onRejected抛出异常e,则promise2必须以拒绝e为拒因
2.4.3 如果onFulfilled不是函数并且promise1被满足,则必须用promise1被满足的终值来满足promise2,并把这个终值传递 给promise2.then 的成功回调函数的第一个参数
2.4.4 如果onRejected不是函数并被promise1拒绝,则必须用promise1被拒绝的拒因来拒绝promise2,并把这个拒因传递给 promise2.then 的失败回调函数的第一个参数
复制代码
啃完规范之后,很无聊,接下来让我们一个一个分析解决
分析:
- 由上面的规范,我们知道,then接受两个可选的参数,理想状态肯定两个都是函数啦,我们先实现理想状态的情况
- 当resolve(value)时,会执行then的onFulfilled函数,并且把value作为终值,传给onFulfilled函数,reject(reason)同理
- 这里我们先解决规范2中的 2.2 2.3 => onFulfilled, onRejected都是函数的理想状态
更新promise.js函数 ----添加then方法
class Promise {
constructor(executor) {
if(typeof executor !== 'function') {
throw new TypeError(`Promise resolver ${executor} is not a function`)
}
this.initValue()
this.initBind()
executor(this.resolve,this.reject)
}
initValue() {
this.state = Promise.PENDING;
this.value = null;
this.reason = null;
}
initBind() {
this.resolve = this.resolve.bind(this)
this.reject = this.reject.bind(this)
}
resolve(value) {
if(this.state === Promise.PENDING) {
this.state = Promise.FULFILLED;
this.value = value;
}
}
reject(reason) {
if(this.state === Promise.PENDING) {
this.state = Promise.REJECTED;
this.reason = reason;
}
}
// 添加then 方法
then(onFulfilled, onRejected) {
if(this.state === Promise.FULFILLED) {
onFulfilled(this.value)
}
if(this.state === Promise.REJECTED) {
onRejected(this.reason)
}
}
}
Promise.PENDING = 'pending';
Promise.FULFILLED = 'fulfilled';
Promise.REJECTED = 'rejected';
module.exports = Promise;
复制代码
- 接下来来验证我们的成果,在index.js 和 test.js 文件中分别运行下面代码
let onFulfulled = value1 => {
console.log('value1', value1)
}
let onRejected = reason1 => {
console.log('reason1', reason1)
}
let promise1 = new Promise((resolve, reject) => {
console.log('成功')
resolve(1)
});
promise1.then(onFulfulled, onRejected)
// 在index.js 和 test.js 的结果都是:
// 成功
// value1 1
复制代码
- 看着结果,好像成功了呢,老厉害了,非也,我们再看看下面代码,在index.js和test.js中分别运行它
示例1
let onFulfulled = value1 => {
console.log('value1', value1)
}
let onRejected = reason1 => {
console.log('reason1', reason1)
}
console.log(1)
let promise1 = new Promise((resolve, reject) => {
console.log(2)
resolve(3)
});
promise1.then(onFulfulled, onRejected)
console.log(4)
// 在index.js文件中结果如下
// 1 2 4 value1 3
// 在test.js中结果如下
// 1 2 value1 3 4
复制代码
分析
- 我们知道,在标准的Promise中,then是一个异步微任务,而在我们的promise.js中,then是个同步函数,当resolve(3)的时候,promise1的状态由待定变成已解决,即state="fulfulled",接着执行then方法,所以会先打印出3,才打印出 4。
问题1:
- promise.js中的then是个同步函数,会先执行onFulfulled,然后再执行console.log(4)。
解决方式
- 用setTimeout使onFulfulled和onRejected变成异步任务。
更新promise.js----------给onFulfulled, onRejected分别加 setTimeout延迟执行
class Promise {
constructor(executor) {
if (typeof executor !== 'function') {
throw new TypeError(`Promise resolver ${executor} is not a function`)
}
this.initValue()
this.initBind()
executor(this.resolve, this.reject)
}
initValue() {
this.state = Promise.PENDING;
this.value = null;
this.reason = null;
}
initBind() {
this.resolve = this.resolve.bind(this)
this.reject = this.reject.bind(this)
}
resolve(value) {
if (this.state === Promise.PENDING) {
this.state = Promise.FULFILLED;
this.value = value;
}
}
reject(reason) {
if (this.state === Promise.PENDING) {
this.state = Promise.REJECTED;
this.reason = reason;
}
}
then(onFulfilled, onRejected) {
if (this.state === Promise.FULFILLED) {
// 添加 setTimeout 延迟 onFulfilled执行
setTimeout(() => {
onFulfilled(this.value)
})
}
if (this.state === Promise.REJECTED) {
// 添加 setTimeout 延迟 onRejected执行
setTimeout(() => {
onRejected(this.reason)
})
}
}
}
Promise.PENDING = 'pending';
Promise.FULFILLED = 'fulfilled';
Promise.REJECTED = 'rejected';
module.exports = Promise;
复制代码
示例2
-
接下来我们来看另外一种情况:这也是平常我们用promise的常见的情况之一,当我们用ajax,或者**setTimeout **这种异步任务,等异步任务执行完,再执行resolve或reject的时候,会出现什么情况?
-
看看下面代码,分别在index.js 和 test.js 文件中运行:
console.log(1)
new Promise((resolve, reject) => {
console.log(2)
// 重点注意 setTimeout
setTimeout(()=>{
resolve(3)
})
}).then(value => {
console.log('value',value)
},reason => {
console.log('reason',reason)
})
console.log(4)
// 在index.js中结果为: 1 2 4 value 3
// 在test.js中结果为: 1 2 4 => 也就是说 then方法执行了没效果
复制代码
分析
- 看示例2,当代码执行到 console.log(2) 之后,遇到 setTimeout 会将resolve(3)放到异步任务队列中,此时resolve(3)并不会立即执行,Promise的状态依然还是待定状态(pending),这个时候执行了then方法,这样并不会执行onFulfulled或onRejected函数。
问题2
- 用setTimeout(()=>{resolve(3)}) then方法执行没意义。
解决
- 结合示例2,我们可以在promise.js中定义两个数组onFulfilledCallbacks=[],onRejectedCallbacks=[]
- 当promise处于等待态就执行then方法,那就把onFulfulled和onRejected函数分别放入这两个数组中
- 等到一定时间之后**(同步任务执行完),再执行resolve(3),再将onFulfilledCallbacks数组里的函数依次执行**。reject 和 onRejectedCallbacks也同理。
- 这种方式有点像发布订阅模式,你品,你细品,修改代码如下:
class Promise {
constructor(executor) {
if (typeof executor !== 'function') {
throw new TypeError(`Promise resolver ${executor} is not a function`)
}
this.initValue()
this.initBind()
executor(this.resolve, this.reject)
}
initValue() {
this.state = Promise.PENDING;
this.value = null;
this.reason = null;
// 添加 onFulfilledCallbacks onRejectedCallbacks
this.onFulfilledCallbacks = [];
this.onRejectedCallbacks = [];
}
initBind() {
this.resolve = this.resolve.bind(this)
this.reject = this.reject.bind(this)
}
resolve(value) {
if (this.state === Promise.PENDING) {
this.state = Promise.FULFILLED;
this.value = value;
// 添加 依次执行
this.onFulfilledCallbacks.forEach(fn => fn(this.value))
}
}
reject(reason) {
if (this.state === Promise.PENDING) {
this.state = Promise.REJECTED;
this.reason = reason;
// 添加 依次执行
this.onRejectedCallbacks.forEach(fn => fn(this.reason))
}
}
then(onFulfilled, onRejected) {
if (this.state === Promise.FULFILLED) {
setTimeout(() => {
onFulfilled(this.value)
})
}
if (this.state === Promise.REJECTED) {
setTimeout(() => {
onRejected(this.reason)
})
}
// 还是等待态的时候就执行then方法处理
if (this.state === Promise.PENDING) {
this.onFulfilledCallbacks.push((value) => {
setTimeout(() => {
onFulfilled(value)
})
})
this.onRejectedCallbacks.push((reason) => {
setTimeout(() => {
onRejected(reason)
})
})
}
}
}
Promise.PENDING = 'pending';
Promise.FULFILLED = 'fulfilled';
Promise.REJECTED = 'rejected';
module.exports = Promise;
复制代码
示例3
-
只有你想不到,没有标准Promise做不到的,假设我们在new Promise中传入的函数executor中,抛出错误
new Promise((resolve, reject) => { throw('哈哈') resolve(1) }).then(value => { console.log('value',value) },reason => { console.log('reason',reason) }) // index.js => reason 哈哈 // test.js => Uncaught 哈哈 抛出错误 复制代码
分析
- 在我们的promise.js中,执行 executor函数的时候,如果遇到错误,就会直接抛出,没有经过Promise类处理
问题3
- 执行 executor函数,如果遇到程序错误,执行结果不一样
解决
- 用try catch组合捕获executor函数异常,然后拒绝(reject)出去即可
更新promise.js文件 ----添加捕获executor函数异常
class Promise {
constructor(executor) {
if (typeof executor !== 'function') {
throw new TypeError(`Promise resolver ${executor} is not a function`)
}
this.initValue()
this.initBind()
// 此处添加 try catch
try {
executor(this.resolve, this.reject)
} catch (err) {
this.reject(err)
}
}
initValue() {
this.state = Promise.PENDING;
this.value = null;
this.reason = null;
this.onFulfilledCallbacks = [];
this.onRejectedCallbacks = [];
}
initBind() {
this.resolve = this.resolve.bind(this)
this.reject = this.reject.bind(this)
}
resolve(value) {
if (this.state === Promise.PENDING) {
this.state = Promise.FULFILLED;
this.value = value;
this.onFulfilledCallbacks.forEach(fn => fn(this.value))
}
}
reject(reason) {
if (this.state === Promise.PENDING) {
this.state = Promise.REJECTED;
this.reason = reason;
this.onRejectedCallbacks.forEach(fn => fn(this.reason))
}
}
then(onFulfilled, onRejected) {
if (this.state === Promise.FULFILLED) {
setTimeout(() => {
onFulfilled(this.value)
})
}
if (this.state === Promise.REJECTED) {
setTimeout(() => {
onRejected(this.reason)
})
}
if (this.state === Promise.PENDING) {
this.onFulfilledCallbacks.push((value) => {
setTimeout(() => {
onFulfilled(value)
})
})
this.onRejectedCallbacks.push((reason) => {
setTimeout(() => {
onRejected(reason)
})
})
}
}
}
Promise.PENDING = 'pending';
Promise.FULFILLED = 'fulfilled';
Promise.REJECTED = 'rejected';
module.exports = Promise;
复制代码
- 再运行下代码示例3代码 问题3解决
链式调用
我们用promise的时候,可以这么用:promise.then().then() ....
再看看上面提到的规范,我们还没解决的有
2 then方法
用法:promise.then(onFulfilled, onRejected)
2.1 这两个onFulfilled和onRejected是可选的参数
2.1.1 如果onFulfilled不是函数,则必须忽略它 => 其实不是忽略,是将他重新赋值一个函数 下文会讲到
2.1.2 如果onRejected不是函数,则必须忽略它 => 其实不是忽略,是将他重新赋值一个函数 下文会讲到
// 2.2 2.3 上文已解决
2.4 then 必须返回一个promise实例,这样才可以实现链式调用,后文会讲
我们看看下面例子:
eg: promise2 = promise1.then(onFulfilled, onRejected);
接着,我们将定义的函数resolvePromise 后文会讲到
2.4.1 如果onFulfilled或onRejected的返回值为x,则运行函数resolvePromise(promise2, x)
2.4.2 如果onFulfilled或onRejected抛出异常e,则promise2必须以拒绝e为拒因
2.4.3 如果onFulfilled不是函数并且promise1被满足,则必须用promise1被满足的终值来满足promise2,并把这个终值传递 给promise2.then 的成功回调函数的第一个参数
2.4.4 如果onRejected不是函数并被promise1拒绝,则必须用promise1被拒绝的拒因来拒绝promise2,并把这个拒因传递给 promise2.then 的失败回调函数的第一个参数
复制代码
- 为了更好的理解,我们先解决规范2.4这个大魔头。
- promise2 = promise1.then(onFulfilled, onRejected)
分析
-
规范 2.4.1:如果onFulfilled或onRejected返回x,则运行函数resolvePromise(promise2, x)
-
先看下面例子:
let promise1 = new Promise((resolve, reject) => { resolve(1) }) promise1.then(value => { // 重点注意 x return x; console.log('value',value) },reason => { console.log('reason',reason) }) 复制代码
-
注意,return x 。这个x 可以取所有符合javascript数据类型的值。
-
万事开头难,不管怎么样,先定义resolvePromise函数
-
更新 promise.js === 定义resolvePromise函数,将onFulfilled或onRejected返回值赋值给变量x
class Promise {
constructor(executor) {
if (typeof executor !== 'function') {
throw new TypeError(`Promise resolver ${executor} is not a function`)
}
this.initValue()
this.initBind()
try {
executor(this.resolve, this.reject)
} catch (err) {
this.reject(err)
}
}
initValue() {
this.state = Promise.PENDING;
this.value = null;
this.reason = null;
this.onFulfilledCallbacks = [];
this.onRejectedCallbacks = [];
}
initBind() {
this.resolve = this.resolve.bind(this)
this.reject = this.reject.bind(this)
}
resolve(value) {
if (this.state === Promise.PENDING) {
this.state = Promise.FULFILLED;
this.value = value;
this.onFulfilledCallbacks.forEach(fn => fn(this.value))
}
}
reject(reason) {
if (this.state === Promise.PENDING) {
this.state = Promise.REJECTED;
this.reason = reason;
this.onRejectedCallbacks.forEach(fn => fn(this.reason))
}
}
then(onFulfilled, onRejected) {
if (this.state === Promise.FULFILLED) {
setTimeout(() => {
// 返回值给x
const x = onFulfilled(this.value)
})
}
if (this.state === Promise.REJECTED) {
setTimeout(() => {
// 返回值给x
const x = onRejected(this.reason)
})
}
if (this.state === Promise.PENDING) {
this.onFulfilledCallbacks.push((value) => {
setTimeout(() => {
// 返回值给x
const x = onFulfilled(value)
})
})
this.onRejectedCallbacks.push((reason) => {
setTimeout(() => {
// 返回值给x
const x = onRejected(reason)
})
})
}
}
}
Promise.PENDING = 'pending';
Promise.FULFILLED = 'fulfilled';
Promise.REJECTED = 'rejected';
// 定义resolvePromise函数
Promise.resolvePromise = function (promise2, x) {
}
module.exports = Promise;
复制代码
分析
-
规范2.4: then必须返回一个promise实例
- 在then方法里面返回一个promise实例
-
promise2 = promise1.then(onFulfilled, onRejected);
-
规范2.4.2: 如果onFulfilled或onRejected抛出异常e,则promise2必须以拒绝e为拒因
-
分别用try catch捕获onFulfilled和onRejected异常,然后拒绝(reject)给promise2
到这里,先更新 一下 promise.js === then方法返回一个promise实例,try,catch捕获异常,拒绝出去
class Promise {
constructor(executor) {
if (typeof executor !== 'function') {
throw new TypeError(`Promise resolver ${executor} is not a function`)
}
this.initValue()
this.initBind()
try {
executor(this.resolve, this.reject)
} catch (err) {
this.reject(err)
}
}
initValue() {
this.state = Promise.PENDING;
this.value = null;
this.reason = null;
this.onFulfilledCallbacks = [];
this.onRejectedCallbacks = [];
}
initBind() {
this.resolve = this.resolve.bind(this)
this.reject = this.reject.bind(this)
}
resolve(value) {
if (this.state === Promise.PENDING) {
this.state = Promise.FULFILLED;
this.value = value;
this.onFulfilledCallbacks.forEach(fn => fn(this.value))
}
}
reject(reason) {
if (this.state === Promise.PENDING) {
this.state = Promise.REJECTED;
this.reason = reason;
this.onRejectedCallbacks.forEach(fn => fn(this.reason))
}
}
then(onFulfilled, onRejected) {
// 定义新的promise实例 promise2 并且 做为then方法的返回值
let promise2 = new Promise((resolve2, reject2) => {
if (this.state === Promise.FULFILLED) {
setTimeout(() => {
// 捕获异常
try {
const x = onFulfilled(this.value)
} catch (err) {
reject2(err)
}
})
}
if (this.state === Promise.REJECTED) {
setTimeout(() => {
// 捕获异常
try {
const x = onRejected(this.reason)
} catch (err) {
reject2(err)
}
})
}
if (this.state === Promise.PENDING) {
this.onFulfilledCallbacks.push((value) => {
setTimeout(() => {
// 捕获异常
try {
const x = onFulfilled(value)
} catch (err) {
reject2(err)
}
})
})
this.onRejectedCallbacks.push((reason) => {
setTimeout(() => {
// 捕获错误
try {
const x = onRejected(reason)
} catch (err) {
reject2(err)
}
})
})
}
})
return promise2
}
}
Promise.PENDING = 'pending';
Promise.FULFILLED = 'fulfilled';
Promise.REJECTED = 'rejected';
Promise.resolvePromise = function (promise2, x) {
}
module.exports = Promise;
复制代码
不要停下来,咱们继续
分析
-
promise2 = promise1.then(onFulfilled, onRejected)
-
规范 2.4.3 如果onFulfilled不是函数并且promise1被满足,则promise1必须用与 相同的值来满足promise2
- 解决: 这个要判断传进来的onFulfilled是不是函数,如果不是, 将解决promise1的value值,解决(resolve)promise2
-
规范 2.4.4 如果onRejected不是函数并被promise1拒绝,则promise1必须以与 相同的拒因拒绝promise2
- 解决: 这个要判断传进来的onRejected是不是函数,如果不是,将拒绝promise1的reason值,解决(resolve)promise2(注意,这里不是拒绝reject)
代码思路
-
我们可以先判断then传进来的两个方法onFulfulled, onRejected 是否为函数,如果不是函数 就分别给这两个函数赋新的函数
-
新函数分别是 onFulfulled = (value) => value onRejected = (reason) => { throw( reason) }
-
将 onFulfulled或者onRejected处理的值(执行onFulfulled或者onRejected函数的返回值x),解决(reolve)promise2
-
其实这里也解决了下面的规范
2.1 这两个onFulfilled和onRejected可选的参数 2.1.1 如果onFulfilled不是函数,则必须忽略它 => 其实不是忽略,是将他重新赋值一个函数 下文会讲到 2.1.2 如果onRejected不是函数,则必须忽略它 => 其实不是忽略,是将他重新赋值一个函数 下文会讲到 复制代码
更新promise.js 代码 === 判断then方法传入的参数是不是函数,并加以处理,把promise1的拒绝和解决,全部解决给promise2
class Promise {
constructor(executor) {
if (typeof executor !== 'function') {
throw new TypeError(`Promise resolver ${executor} is not a function`)
}
this.initValue()
this.initBind()
try {
executor(this.resolve, this.reject)
} catch (err) {
this.reject(err)
}
}
initValue() {
this.state = Promise.PENDING;
this.value = null;
this.reason = null;
this.onFulfilledCallbacks = [];
this.onRejectedCallbacks = [];
}
initBind() {
this.resolve = this.resolve.bind(this)
this.reject = this.reject.bind(this)
}
resolve(value) {
if (this.state === Promise.PENDING) {
this.state = Promise.FULFILLED;
this.value = value;
this.onFulfilledCallbacks.forEach(fn => fn(this.value))
}
}
reject(reason) {
if (this.state === Promise.PENDING) {
this.state = Promise.REJECTED;
this.reason = reason;
this.onRejectedCallbacks.forEach(fn => fn(this.reason))
}
}
then(onFulfilled, onRejected) {
// 判断是否为函数
if (typeof onFulfilled !== 'function') {
onFulfilled = (value) => value;
}
// 判断是否为函数
if (typeof onRejected !== 'function') {
onRejected = (reason) => { throw reason };
}
let promise2 = new Promise((resolve2, reject2) => {
if (this.state === Promise.FULFILLED) {
setTimeout(() => {
try {
const x = onFulfilled(this.value)
// 解决promise2
resolve2(x)
} catch (err) {
reject2(err)
}
})
}
if (this.state === Promise.REJECTED) {
setTimeout(() => {
try {
const x = onRejected(this.reason)
// 解决promise2
resolve2(x)
} catch (err) {
reject2(err)
}
})
}
if (this.state === Promise.PENDING) {
this.onFulfilledCallbacks.push((value) => {
setTimeout(() => {
try {
const x = onFulfilled(value)
// 解决promise2
resolve2(x)
} catch (err) {
reject2(err)
}
})
})
this.onRejectedCallbacks.push((reason) => {
setTimeout(() => {
try {
const x = onRejected(reason)
// 解决promise2
resolve2(x)
} catch (err) {
reject2(err)
}
})
})
}
})
return promise2
}
}
Promise.PENDING = 'pending';
Promise.FULFILLED = 'fulfilled';
Promise.REJECTED = 'rejected';
Promise.resolvePromise = function (promise2, x) {
}
module.exports = Promise;
复制代码
-
为了加深理解
-
promise2 = promise1.then(onFulfilled, onRejected)
我们分别在index.js 和 test.js 运行下面的代码
// 这种情况是promise1 已被解决 但promise1.then 的 onFulfulled不是函数,是个 1 let promise1 = new Promise((resolve, reject) => { resolve('解决但onFulfulled不是函数') }) // 注意下面的1 let promise2 = promise1.then(1,reason1 => { console.log('reason1', reason1) }) promise2.then(value1 => { console.log('value1',value1) }, reason1 => { console.log('reason1', reason1) }) // index.js 输出 value1 解决但onFulfulled不是函数 // test.js 输出 value1 解决但onFulfulled不是函数 复制代码
- 结果符合我们的预期
接着继续在index,js 和 test.js文件中运行
// 这种情况是promise1 已被拒绝 但promise1.then 的 onRejected不是函数,没有传参 let promise1 = new Promise((resolve, reject) => { reject('拒绝但onRejected不是函数') }) // 注意这里then方法没有传拒绝回调函数 let promise2 = promise1.then(value =>{ console.log('value',value) }) promise2.then(value1 => { console.log('value1',value1) }, reason1 => { console.log('reason1', reason1) }) // index.js 输出 reason1 拒绝但onRejected不是函数 // test.js 输出 reason1 拒绝但onRejected不是函数 复制代码
- 结果也符合我们的预期
-
这看到这里,可能有些人会想,为啥promise1的then中onRejected不是函数,且promise1被拒绝(rejected),但仍然**要resolve(x)给promise2?**如下面代码所示:
if (this.state === Promise.REJECTED) {
setTimeout(() => {
try {
const x = onRejected(this.reason)
// 其实下面不会执行
resolve2(x)
} catch (err) {
reject2(err)
}
})
}
复制代码
- 其实根本不会执行resolve2(x),前面我们判断onRejected不是函数的时候,执行了 onRejected = (reason) => { throw reason };,也就是说错误被捕获了,这里实际上是执行 reject2(err)
完结 第二部分的规范也已经完全解决了
ok ,你可以给自己来杯热茶,预祝胜利了,你已经完成了百分之八九十了,再坚持一下,就快修炼成功了,还记得我们前面提到的resolvePromise函数吗?
神奇的resolvePromise函数
- 前面我们知道,onFulfilled或onRejected返回值x,x可以是符合javascript所有类型的值,我们现在考虑一个问题:
- 如果x是个promise实例,或者x是个函数,亦或者x是个对象,我们应该怎么处理?这就不得不提前面提到的resolvePromise函数了
- 咱们先看看规范怎么说的
- 规范3:
3 resolvePromise函数
3.1 如果promise和x引用同一个对象,promise以一个TypeError类型e为理由拒绝
3.2 如果x是个promise实例
3.2.1 promise必须保持挂起状态,直到x完成或被拒绝。
3.2.2 何时x完成,则promise用相同的值完成
3.2.3 何时x被拒绝,promise以同样的理由拒绝
3.3 x是一个对象或函数
3.3.1 看看x有没有then属性 有的话 让then = x.then 如果then是个函数 执行then 并且把then函数的this指向x,then还 有可能抛出错误,用try catch捕获 然后reject出去
3.3.2 如果x没有then属性,那么当成普通的值resolve出去,完成解决
3.4 如果x不是拥有then方法的对象或者promise实例,直接解决resolve出去
3.5 提倡但是不建议递归调用resolvePromise,不能无限递归调用 应该有一个标志called,来标志是否解决或者拒绝过
复制代码
- 我们先来看看一个例子,我们分别在index.js test.js文件中运行下面代码
let promise1 = new Promise((resolve, reject) => {
resolve(1)
})
let promise2 = promise1.then(value => {
// 关键代码 在 promise1的成功回调函数中 return一个promise实例
return new Promise((resolve, reject) => {
resolve(2)
})
}, reason => {
console.log('reason', reason)
})
promise2.then(value1 => {
console.log('value1', value1)
}, reason1 => {
console.log('reason1', reason1)
})
// 在index.js中 输出 value1 2
// 在test.js文件中输出 value1 Promise {
// state: 'fulfilled',
// value: 2,
// reason: null,
// onFulfilledCallbacks: [],
// onRejectedCallbacks: [],
// resolve: [Function: bound resolve],
// reject: [Function: bound reject]
// }
复制代码
- 也就是说,这是当x是一个promise实例的情况,我们写的promise.js文件会原样输出promise实例,而标准的promise是把这个promise执行完(解决或拒绝)之后抛出去给它的下一个promise实例,往复循环。
- 因此,这个 x不能直接resolve(x),要分情况讨论,这也是规范3提到的:x可以分为promise实例,对象或者函数,普通值等的原因,这个时候就要用到我们前面提到的resolvePromise函数了,先更新一波代码
更新promise.js代码=====让x交给resolvePromise函数处理
class Promise {
constructor(executor) {
if (typeof executor !== 'function') {
throw new TypeError(`Promise resolver ${executor} is not a function`)
}
this.initValue()
this.initBind()
try {
executor(this.resolve, this.reject)
} catch (err) {
this.reject(err)
}
}
initValue() {
this.state = Promise.PENDING;
this.value = null;
this.reason = null;
this.onFulfilledCallbacks = [];
this.onRejectedCallbacks = [];
}
initBind() {
this.resolve = this.resolve.bind(this)
this.reject = this.reject.bind(this)
}
resolve(value) {
if (this.state === Promise.PENDING) {
this.state = Promise.FULFILLED;
this.value = value;
this.onFulfilledCallbacks.forEach(fn => fn(this.value))
}
}
reject(reason) {
if (this.state === Promise.PENDING) {
this.state = Promise.REJECTED;
this.reason = reason;
this.onRejectedCallbacks.forEach(fn => fn(this.reason))
}
}
then(onFulfilled, onRejected) {
if (typeof onFulfilled !== 'function') {
onFulfilled = (value) => value;
}
if (typeof onRejected !== 'function') {
onRejected = (reason) => { throw reason };
}
let promise2 = new Promise((resolve2, reject2) => {
if (this.state === Promise.FULFILLED) {
setTimeout(() => {
try {
// 让x交给resolvePromise函数处理
const x = onFulfilled(this.value)
Promise.resolvePromise(promise2,x,resolve2,reject2)
// resolve2(x)
} catch (err) {
reject2(err)
}
})
}
if (this.state === Promise.REJECTED) {
setTimeout(() => {
try {
// 让x交给resolvePromise函数处理
const x = onRejected(this.reason)
Promise.resolvePromise(promise2,x,resolve2,reject2)
// resolve2(x)
} catch (err) {
reject2(err)
}
})
}
if (this.state === Promise.PENDING) {
this.onFulfilledCallbacks.push((value) => {
setTimeout(() => {
try {
// 让x交给resolvePromise函数处理
const x = onFulfilled(value)
Promise.resolvePromise(promise2,x,resolve2,reject2)
// resolve2(x)
} catch (err) {
reject2(err)
}
})
})
this.onRejectedCallbacks.push((reason) => {
setTimeout(() => {
try {
// 让x交给resolvePromise函数处理
const x = onRejected(reason)
Promise.resolvePromise(promise2,x,resolve2,reject2)
// resolve2(x)
} catch (err) {
reject2(err)
}
})
})
}
})
return promise2
}
}
Promise.PENDING = 'pending';
Promise.FULFILLED = 'fulfilled';
Promise.REJECTED = 'rejected';
Promise.resolvePromise = function (promise2,x,resolve2,reject2) {
}
module.exports = Promise;
复制代码
再结合规范3,我们一步一步分析处理
-
我们可以先初始化条件判断的代码如下:
Promise.resolvePromise = function (promise2, x,resolve2,reject2) { // x是个和promise2相同 if (promise2 === x) { } // x是个Promise实例 if (x instanceof Promise) { } // x是个对象或者函数 else if(x !== null && (typeof x === 'function' || typeof x === 'object') { } // x是个普通值 else { } } 复制代码
-
规范3.1: 如果promise和x引用同一个对象,promise以一个TypeError类型e为理由拒绝
- 可以再index.js文件中运行如下
let promise1 = new Promise((resolve,reject) => { resolve(1) }) let promise2 = promise1.then(value =>{ return promise2 }) // 运行结果 TypeError: Chaining cycle detected for promise 复制代码
- promise2 永远等待promise2执行完才会执行,自己等自己,这不是陷入了死循环了吗? (下文解决)
-
规范3.4 :如果x不是promise实例或者不是一个拥有then方法的对象,直接解决(resolve)
-
结合 规范3.1 规范3.4 更新resolvePromise函数如下:
Promise.resolvePromise = function (promise2, x,resolve2,reject2) {
// x是个和promise相同
if (promise2 === x) {
// 规范3.1处理
reject2(new TypeError('Chaining cycle detected for promise'))
}
// x是个Promise实例
if (x instanceof Promise) {
}
// x是个对象或者函数
else if(x !== null && (typeof x === 'function' || typeof x === 'object') {
}
// x是个普通值 =》 不是promise实例或者不是一个拥有then方法的对象
else {
// 看上面规范 3.4 如果x不是拥有then方法的对象或者promise实例,直接解决(resolve)出去
resolve2(x)
}
}
复制代码
-
规范3.2: 如果x是个promise实例 3.2.1 promise必须保持挂起状态,直到x完成或被拒绝。 3.2.2 何时x完成,则promise用相同的值完成 3.2.3 何时x被拒绝,promise以同样的理由拒绝
- 说白了就是等待这个promise实例解决或拒绝再处理出去
Promise.resolvePromise = function (promise2, x,resolve2,reject2) { // x是个和promise相同 if (promise2 === x) { // 规范3.1 reject2(new TypeError('Chaining cycle detected for promise')) } // x是个Promise实例 if (x instanceof Promise) { x.then(value => { // 这里不是resolve(value) 下文有说明 Promise.resolvePromise(promise2, value, resolve2, reject2) }, reason => { reject2(reason) }) } // x是个对象或者函数 else if(x !== null && (typeof x === 'function' || typeof x === 'object') { } // x是个普通值 else { // 看上面规范 3.4 如果x不是拥有then方法的对象或者promise实例,直接解决resolve出去 resolve2(x) } } 复制代码
- 说明:上面代码没有用resolve(value)而用Promise.resolvePromise(promise2, value, resolve2, reject2) 就是防止疯狂套娃情况,比如return 的promise实例之后,其then方法里面的成功回调函数又 return一个 promise实例 ,如此往复。大哥你可真行啊,这么能套娃,但是,不管你怎么套娃,不考虑异常报错的情况下,我们最终都会处理到x是个普通值,然后resolve(x)出去。
-
规范3.3:x是一个对象或函数 3.3.1 看看x有没有then属性,有的话,让then = x.then,如果then是个函数,执行then 并且把then函数的this指向x,同时用try catch捕获then函数执行抛出错误,,然后拒绝(reject)出去。 3.3.2 如果x没有then属性,那么当成普通的值resolve出去,完成解决
按照规范更新代码
Promise.resolvePromise = function (promise2, x,resolve2,reject2) {
// x是个和promise相同
if (promise2 === x) {
reject2(new TypeError('Chaining cycle detected for promise'))
}
// x是个Promise实例
if (x instanceof Promise) {
x.then(value => {
Promise.resolvePromise(promise2, value, resolve2, reject2)
}, reason => {
reject2(reason)
})
}
// x是个对象或者函数
else if(x !== null && (typeof x === 'function' || typeof x === 'object')) {
// 规范3.3 try catch 捕获then函数错误
// then成功回调函数返回值再交给resolvePromise(promise2, value, resolve2, reject2)
try {
const then = x.then;
if (typeof then === 'function') {
then.call(x, value => {
Promise.resolvePromise(promise2, value, resolve2, reject2)
}, reason => {
reject2(reason)
})
} else {
resolve2(x)
}
}catch(err) {
reject2(err)
}
}
// x是个普通值
else {
// 看上面规范 3.4 如果x不是拥有then方法的对象或者promise实例,直接解决resolve出去
resolve2(x)
}
}
复制代码
- 3.5 规范不建议resolvePromise无限递归调用 应该有一个标志called,调用一次之后不再调用了
更新代码如下====这也是resolvePromise最终代码了
Promise.resolvePromise = function (promise2, x, resolve2, reject2) {
// x是个和promise相同
if (promise2 === x) {
reject2(new TypeError('Chaining cycle detected for promise'))
}
// called 标志是否处理过(成功或者拒绝)
let called = false;
// x是个Promise实例
if (x instanceof Promise) {
x.then(value => {
Promise.resolvePromise(promise2, value, resolve2, reject2)
}, reason => {
reject2(reason)
})
}
// x是个对象或者函数
else if (x !== null && (typeof x === 'function' || typeof x === 'object')) {
try {
const then = x.then;
if (typeof then === 'function') {
then.call(x, value => {
// 新增 called判断
if (called) return
called = true;
Promise.resolvePromise(promise2, value, resolve2, reject2)
}, reason => {
// 新增 called判断
if (called) return
called = true;
reject2(reason)
})
} else {
// 新增 called判断
if (called) return
called = true;
resolve2(x)
}
} catch (err) {
// 新增 called判断
if (called) return
called = true;
reject2(err)
}
}
// x是个普通值
else {
// 看上面规范 3.4 如果x不是拥有then方法的对象或者promise实例,直接解决resolve出去
resolve2(x)
}
}
复制代码
更新promise.js代码 ==== 最终更新
class Promise {
constructor(executor) {
if (typeof executor !== 'function') {
throw new TypeError(`Promise resolver ${executor} is not a function`)
}
this.initValue()
this.initBind()
try {
executor(this.resolve, this.reject)
} catch (err) {
this.reject(err)
}
}
initValue() {
this.state = Promise.PENDING;
this.value = null;
this.reason = null;
this.onFulfilledCallbacks = [];
this.onRejectedCallbacks = [];
}
initBind() {
this.resolve = this.resolve.bind(this)
this.reject = this.reject.bind(this)
}
resolve(value) {
if (this.state === Promise.PENDING) {
this.state = Promise.FULFILLED;
this.value = value;
this.onFulfilledCallbacks.forEach(fn => fn(this.value))
}
}
reject(reason) {
if (this.state === Promise.PENDING) {
this.state = Promise.REJECTED;
this.reason = reason;
this.onRejectedCallbacks.forEach(fn => fn(this.reason))
}
}
then(onFulfilled, onRejected) {
if (typeof onFulfilled !== 'function') {
onFulfilled = (value) => value;
}
if (typeof onRejected !== 'function') {
onRejected = (reason) => { throw reason };
}
let promise2 = new Promise((resolve2, reject2) => {
if (this.state === Promise.FULFILLED) {
setTimeout(() => {
try {
const x = onFulfilled(this.value)
Promise.resolvePromise(promise2, x, resolve2, reject2)
} catch (err) {
reject2(err)
}
})
}
if (this.state === Promise.REJECTED) {
setTimeout(() => {
try {
const x = onRejected(this.reason)
Promise.resolvePromise(promise2, x, resolve2, reject2)
} catch (err) {
reject2(err)
}
})
}
if (this.state === Promise.PENDING) {
this.onFulfilledCallbacks.push((value) => {
setTimeout(() => {
try {
const x = onFulfilled(value)
Promise.resolvePromise(promise2, x, resolve2, reject2)
} catch (err) {
reject2(err)
}
})
})
this.onRejectedCallbacks.push((reason) => {
setTimeout(() => {
try {
const x = onRejected(reason)
Promise.resolvePromise(promise2, x, resolve2, reject2)
} catch (err) {
reject2(err)
}
})
})
}
})
return promise2
}
}
Promise.PENDING = 'pending';
Promise.FULFILLED = 'fulfilled';
Promise.REJECTED = 'rejected';
Promise.resolvePromise = function (promise2, x, resolve2, reject2) {
// x是个和promise相同
if (promise2 === x) {
reject2(new TypeError('Chaining cycle detected for promise'))
}
// called 标志是否处理(成功或者解决值)
let called = false;
// x是个Promise实例
if (x instanceof Promise) {
x.then(value => {
Promise.resolvePromise(promise2, value, resolve2, reject2)
}, reason => {
reject2(reason)
})
}
// x是个对象或者函数
else if (x !== null && (typeof x === 'function' || typeof x === 'object')) {
try {
const then = x.then;
if (typeof then === 'function') {
then.call(x, value => {
if (called) return
called = true;
Promise.resolvePromise(promise2, value, resolve2, reject2)
}, reason => {
if (called) return
called = true;
reject2(reason)
})
} else {
if (called) return
called = true;
resolve2(x)
}
} catch (err) {
if (called) return
called = true;
reject2(err)
}
}
// x是个普通值
else {
// 看上面规范 3.4 如果x不是拥有then方法的对象或者promise实例,直接解决resolve出去
resolve2(x)
}
}
module.exports = Promise;
复制代码
至此,大家伙们,Promise的实现原理终终终终终终终终于搞定了
测试成果
-
我说搞定了就搞定了吗。我们还是让程序说话吧
-
我们可以通过 promises-aplus-tests 来测试我们实现的Promise原理是否满足 Promise/A+ 规范。
-
安装 npm install promises-aplus-tests
-
在promise.js中加入代码:
class Promise { ....这里是我们写的代码 } ... ... module.exports = Promise; // 这一部分是我们加入的代码 Promise.defer = Promise.deferred = function () { let dfd = {} dfd.promise = new Promise((resolve, reject) => { dfd.resolve = resolve dfd.reject = reject }) return dfd } 复制代码
-
运行 npx promises-aplus-tests promise.js
-
测试结果
这波真的大功告成了
参考文献
- www.bilibili.com/video/BV1L4…
- Promise/A+ 规范:promisesaplus.com/