手写一个Promise源码
一步一步实现Promise的过程。 首先实现一个最简单的promise
class MyPromise {
// 立即执行executor函数
constructor(executor) {
executor(this.resolve, this.reject)
}
onFulfilledCallbacks = [] // 定义一个成功的回调数组
onRejectedCallbacks = [] // 定义一个失败的回调数组
status = 'PENDING' // 定义一个状态,根据Promises/A+规范,状态一旦改变不可再改
value = null // 状态变为FULLFULED时,当做.then中的成功回调onFulfilled函数的参数(值)
reason = null // 状态变为REJECTED时,当做.then中的失败回调onRejected函数的参数(原因)
// 当做立即执行函数executor的第一个参数,一般为触发.then成功回调的作用
resolve = (value) => {
if(this.status === 'PENDING') {
// 如果状态为'等待'状态时,先将状态修改为'成功'
this.status = 'FULFILLED'
// 提供给.then成功回调的参数
this.value = value
// 如果是异步调用resolve的情况,this.onFulfilledCallbacks会在.then方法里收集传入的onFulfulled函数
this.onFulfilledCallbacks.forEach(fn => fn(value))
}
}
reject = (reason) => {
if(this.status === 'PENDING') {
// 如果状态为'等待'状态时,先将状态修改为'失败'
this.status = 'REJECTED'
// 提供给.then失败回调的参数(原因)
this.reason = reason
// 如果是异步调用reject的情况,this.onRejectedCallbacks会在.then方法里收集传入的onRejected函数
this.onRejectedCallbacks.forEach(fn => fn(reason))
}
}
then = function(onFulfilled, onRejected) {
if(this.status === 'PENDING') {
// 状态为等待状态时,进入这里说明前面new MyPromise(resolve, reject)中是通过异步的方式调用resolve或reject的。
this.onFulfilledCallbacks.push((value) => { onFulfilled(value) })
this.onRejectedCallbacks.push((reason) => { onRejected(reason) })
}
if(this.status === 'FULFILLED') {
// 说明前面的new MyPromise(resolve, reject)中是通过同步的方式调用resolve的。
onFulfilled(this.value)
}
if(this.status === 'REJECTED') {
// 说明前面的new MyPromise(resolve, reject)中是通过同步的方式调用reject的。
onRejected(this.reason)
}
}
}
这样一个最简单的Promose外壳就完成了。
那接下来我们将去实现.then的链式调用。如何链式调用?可想而知,无非就是.then的时候返回一个新的 Promise实例嘛,接下来让我们在原来的基础上来实现一下。
then(onFulfilled, onRejected) {
const promise2 = new MyPromise((resolve, reject) => {
if(this.status === 'FULFILLED') {
onFulfilled(this.value)
} else if(this.status === 'REJECTED') {
onRejected(this.reason)
} else if(this.status === 'PENDING') {
this.onFulfilledCallbacks.push((value) => { onFulfilled(value) })
this.onRejectedCallbacks.push((reason) => { onRejected(reason) })
}
})
// 将新的Promise返回,给后续的.then调用
return promise2
}
这样链式调用.then().then()就不会报错了,但是我们现在并没办法将上一个.then中resolve或者reject函数的返回值传递给下一个.then里,那该如何解决这个问题,其实很简单,我们要明白.then是谁的then函数,指向谁,谁调用指向谁,所以就是上一个.then的返回的那个Promise实例,那接下来怎么操作就很明显了。将上面的then函数进行修改。
then(onFulfilled, onRejected) {
const promise2 = new MyPromise((resolve, reject) => {
if(this.status === 'FULFILLED') {
let value = onFulfilled(this.value)
resolve(value)
} else if(this.status === 'REJECTED') {
let reason = onRejected(this.reason)
reject(reason)
} else if(this.status === 'PENDING') {
this.onFulfilledCallbacks.push((value) => { onFulfilled(value) })
this.onRejectedCallbacks.push((reason) => { onRejected(reason) })
}
})
// 将新的Promise返回,给后续的.then调用
return promise2
}
让我们测试一下
new MyPromise(resolve => {
resolve(1)
}).then(value => {
console.log(value)
return value + 1
}).then(value => {
console.log(value)
})
输出:1 2
ok,Promise/A+又规定了.then()可以不传参数哇,那我们该如何将上一个.then里面resolve或者reject返回值传递给当前.then的下一个.then呢,也就是如何去做值穿透呢?请看下面代码。
then(onFulfilled, onRejected) {
// 如果onFulfilled不是一个函数的话,我们就将其转为一个函数,并返回上一个promise实例传递过来的值,传递什么返回什么,就能做到值穿透
const realOnFulfilled = typeof onFulfilled === 'function' ? onFulfilled : value => value
// 如果onRejected不是一个函数的话,我们就将其转为一个函数,作用同realOnFulfilled类似
const realOnRejected = typeof onRejected === 'function' ? onRejected : reason => {
throw reason
}
const promise2 = new MyPromise((resolve, reject) => {
if(this.status === 'FULFILLED') {
let value = realOnFulfilled(this.value)
resolve(value)
} else if(this.status === 'REJECTED') {
let reason = realOnRejected(this.reason)
reject(reason)
} else if(this.status === 'PENDING') {
this.onFulfilledCallbacks.push((value) => { realOnFulfilled(value) })
this.onRejectedCallbacks.push((reason) => { realOnRejected(reason) })
}
})
// 将新的Promise返回,给后续的.then调用
return promise2
}
现在测试一下这个功能
new MyPromise(resolve => {
resolve(1)
}).then().then(value => {
console.log(value)
})
ok,假如现在.then中resolve的是一个Promise实例如何解决。现在的版本很明显不适用。那我们又该如何修改?
then(onFulfilled, onRejected) {
// 如果onFulfilled不是一个函数的话,我们就将其转为一个函数,并返回上一个promise实例传递过来的值,传递什么返回什么,就能做到值穿透
const realOnFulfilled = typeof onFulfilled === 'function' ? onFulfilled : value => value
// 如果onRejected不是一个函数的话,我们就将其转为一个函数,作用同realOnFulfilled类似
const realOnRejected = typeof onRejected === 'function' ? onRejected : reason => {
throw reason
}
const promise2 = new MyPromise((resolve, reject) => {
if(this.status === 'FULFILLED') {
// 如果这里x的返回值是Promise实例
let x = realOnFulfilled(this.value)
// 那我们就定义一个resolvePromise函数,其实目的和之前的一样,无非就是调用reslove(参数),只是需要对返回值进行不同的处理
resolvePromise(x, resolve, reject)
} else if(this.status === 'REJECTED') {
// 如果这里x的返回值是Promise实例,同FULFILLED
let x = realOnRejected(this.reason)
resolvePromise(x, resolve, reject)
} else if(this.status === 'PENDING') {
this.onFulfilledCallbacks.push((value) => { realOnFulfilled(value) })
this.onRejectedCallbacks.push((reason) => { realOnRejected(reason) })
}
})
// 将新的Promise返回,给后续的.then调用
return promise2
}
function resolvePromise(x, resolve, reject) {
if (typeof x === 'object' || typeof x === 'function') {
if(x === null) {
return resolve(x)
}
let then
try {
then = x.then
} catch(err) {
reject(err)
}
if (then === 'function') {
try {
then.call(
x,
y => {
// 递归调用resolvePromise函数,假如返回的Promise实例里面又resolve一个Promise实例
resolvePromise(y, resolve, reject)
},
r => {
reject(r)
}
)
} catch(err) {
}
} else {
// 如果是对象值,就直接resolve调用传递就可以了
resolve(x)
}
} else {
// 如果是普通值,就直接resolve调用传递就可以了
resolve(x)
}
}
测试一下
new MyPromise(resolve => {
resolve(1)
}).then(value => {
return new MyPromise(resolve => {
resolve(value + 1)
})
}).then(value => {
console.log(value)
})
ok,成功解决。假如现在我们有代码
let p1 = new MyPromise(resolve => {
resolve(1)
})
p1.then(value => {
return p1
})
这样就会报类型错误Uncaught (in promise) TypeError: Chaining cycle detected for promise #<Promise>,这个时候我们就应该去检查.then里面的onFulfilled的返回值是否跟当前的Promise实例相等,修改下源代码
then(onFulfilled, onRejected) {
// 如果onFulfilled不是一个函数的话,我们就将其转为一个函数,并返回上一个promise实例传递过来的值,传递什么返回什么,就能做到值穿透
const realOnFulfilled = typeof onFulfilled === 'function' ? onFulfilled : value => value
// 如果onRejected不是一个函数的话,我们就将其转为一个函数,作用同realOnFulfilled类似
const realOnRejected = typeof onRejected === 'function' ? onRejected : reason => {
throw reason
}
const promise2 = new MyPromise((resolve, reject) => {
if(this.status === 'FULFILLED') {
// 添加一个queueMicrotask微任务,让读取promise2进入下一个微任务,避免读取不到promise2而报错
queueMicrotask(() => {
// 如果这里x的返回值是Promise实例
let x = realOnFulfilled(this.value)
// 那我们就定义一个resolvePromise函数,其实目的和之前的一样,无非就是调用reslove(参数),只是需要对返回值进行不同的处理
resolvePromise(promise2, x, resolve, reject)
})
} else if(this.status === 'REJECTED') {
// 添加一个queueMicrotask微任务,让读取promise2进入下一个微任务,避免读取不到promise2而报错
queueMicrotask(() => {
try {
// 如果这里x的返回值是Promise实例,同FULFILLED
const x = realOnRejected(this.reason);
resolvePromise(promise2, x, resolve, reject);
} catch (error) {
reject(error)
}
})
} else if(this.status === 'PENDING') {
this.onFulfilledCallbacks.push((value) => { realOnFulfilled(value) })
this.onRejectedCallbacks.push((reason) => { realOnRejected(reason) })
}
})
// 将新的Promise返回,给后续的.then调用
return promise2
}
function resolvePromise(promise, x, resolve, reject) {
// 相同reject错误
if (promise === x) {
return reject(new TypeError('Chaining cycle detected for promise #<Promise>'))
}
if (typeof x === 'object' || typeof x === 'function') {
if(x === null) {
return resolve(x)
}
let then
try {
then = x.then
} catch(err) {
reject(err)
}
if (then === 'function') {
try {
then.call(
x,
y => {
// 递归调用resolvePromise函数,假如返回的Promise实例里面又resolve一个Promise实例
resolvePromise(promise, y, resolve, reject)
},
r => {
reject(r)
}
)
} catch(err) {
reject(err);
}
} else {
// 如果是对象值,就直接resolve调用传递就可以了
resolve(x)
}
} else {
// 如果是普通值,就直接resolve调用传递就可以了
resolve(x)
}
}
ok,没什么问题,最后贴上完整代码
class MyPromise {
constructor(executor) {
try {
executor(this.resolve, this.reject)
} catch(err) {
throw err
}
}
onFulfilledCallbacks = []
onRejectedCallbacks = []
status = 'PENDING'
value = null
reason = null
resolve = (value) => {
if(this.status === 'PENDING') {
this.status = 'FULFILLED'
this.value = value
this.onFulfilledCallbacks.forEach(fn => fn(value))
}
}
reject = (reason) => {
if(this.status === 'PENDING') {
this.status = 'REJECTED'
this.reason = reason
this.onRejectedCallbacks.forEach(fn => fn(reason))
}
}
then(onFulfilled, onRejected) {
// 如果onFulfilled不是一个函数的话,我们就将其转为一个函数,并返回上一个promise实例传递过来的值,传递什么返回什么,就能做到值穿透
const realOnFulfilled = typeof onFulfilled === 'function' ? onFulfilled : value => value
// 如果onRejected不是一个函数的话,我们就将其转为一个函数,作用同realOnFulfilled类似
const realOnRejected = typeof onRejected === 'function' ? onRejected : reason => {
throw reason
}
const promise2 = new MyPromise((resolve, reject) => {
const fulfilledMicrotask = () => {
// 添加一个queueMicrotask微任务,让读取promise2进入下一个微任务,避免读取不到promise2而报错
queueMicrotask(() => {
try{
// 如果这里x的返回值是Promise实例
let x = realOnFulfilled(this.value)
// 那我们就定义一个resolvePromise函数,其实目的和之前的一样,无非就是调用reslove(参数),只是需要对返回值进行不同的处理
resolvePromise(promise2, x, resolve, reject)
} catch(error) {
reject(error)
}
})
}
const rejectedMicrotask = () => {
// 添加一个queueMicrotask微任务,让读取promise2进入下一个微任务,避免读取不到promise2而报错
queueMicrotask(() => {
try {
// 如果这里x的返回值是Promise实例,同FULFILLED
const x = realOnRejected(this.reason);
resolvePromise(promise2, x, resolve, reject);
} catch (error) {
reject(error)
}
})
}
if(this.status === 'FULFILLED') {
fulfilledMicrotask()
} else if(this.status === 'REJECTED') {
rejectedMicrotask()
} else if(this.status === 'PENDING') {
this.onFulfilledCallbacks.push(fulfilledMicrotask)
this.onRejectedCallbacks.push(rejectedMicrotask)
}
})
// 将新的Promise返回,给后续的.then调用
return promise2
}
catch (onRejected) {
// 只需要进行错误处理
this.then(undefined, onRejected);
}
}
function resolvePromise(promise, x, resolve, reject) {
// 相同reject错误
if (promise === x) {
return reject(new TypeError('Chaining cycle detected for promise #<Promise>'))
}
if (typeof x === 'object' || typeof x === 'function') {
if(x === null) {
return resolve(x)
}
let then
try {
then = x.then
} catch(err) {
reject(err)
}
if (typeof then === 'function') {
let called = false;
try {
then.call(
x,
y => {
if (called) return;
called = true;
// 递归调用resolvePromise函数,假如返回的Promise实例里面又resolve一个Promise实例
resolvePromise(promise, y, resolve, reject)
},
r => {
if (called) return;
called = true;
reject(r)
}
)
} catch(err) {
if (called) return;
reject(err);
}
} else {
// 如果是对象值,就直接resolve调用传递就可以了
resolve(x)
}
} else {
// 如果是普通值,就直接resolve调用传递就可以了
resolve(x)
}
}
MyPromise.deferred = function () {
var result = {};
result.promise = new MyPromise(function (resolve, reject) {
result.resolve = resolve;
result.reject = reject;
});
return result;
}
module.exports = MyPromise
检查Promise/A+规范,需要新建一个package.json
// package.json
{
"name": "promise",
"version": "1.0.0",
"description": "my promise",
"main": "promise.js",
"scripts": {
"test": "promises-aplus-tests promise"
},
"author": "Wayag",
"license": "ISC",
"devDependencies": {
"promises-aplus-tests": "^2.1.2"
}
}
运行npm run test。
既然讲了Promise源码,那顺便讲一下generator的自动化执行,也async await的简单实现,先来看下generator的手动执行。
function* myGenerator() {
console.log(yield Promise.resolve(1)) // 1
console.log(yield Promise.resolve(2)) // 2
console.log(yield Promise.resolve(3)) // 3
}
const gen = myGenerator()
gen.next().value.then(val => {
console.log(val) // 1
gen.next(val).value.then(val => {
console.log(val) // 2
gen.next(val).value.then(val => {
console.log(val) // 3
gen.next(val)
})
})
})
接下来就是把手动执行的部分换成自动执行,采用递归的方式我们很快就能完成。
function run (generator) {
let g = generator()
function _next (val) {
let res = g.next(val)
if (res.done) return res.value
Promise.resolve(res.value).then(_next ,reject)
}
_next()
}
run(myGenerator)
根据async await最后应该返回一个Promise实例我们需要对源码进行修改
function run (generator) {
return new Promise((resolve, reject) => {
let g = generator()
function _next (val) {
let res = g.next(val)
if (res.done) return resolve(res.value)
Promise.resolve(res.value).then(_next ,reject)
}
_next()
})
}