想要手写Promise,先透过Promise简单应用来了解其本质,它究竟是如何运行与应用的?
Promise本质是个类,可以直接new;里面是执行器executor会将函数立即调用,执行器中有两个函数:resolve和reject。
Promise本质是个类,可以直接new,创建Promise类;使用时传递执行器executor,而执行器会存在两个函数参数:resolve与rejec,两者都可调用。所以在类的构造器constructor中参数为执行器,且存在两个函数,会被执行器立即调用。 二、Promise基本状态
Promise存在三个状态:pending(等待态)、fulfilled(成功态)、rejected(失败态)。
pending(等待态)为初始态,它可以转化为fulfilled(成功态)和rejected(失败态);成功时不可转为其他状态,且必须有一个不可改变的值(value);失败时也不可转为其他状态,且必须有一个不可改变的原因(reason)
resolve表示成功,接收参数value,状态改变为fulfilled,不可再次改变。reject表示失败,接收参数reason,状态改变为rejected,不可再次改变。若是executor函数报错,直接执行reject。
class Promise {
constructor(executor) {
// 初始化
// 状态为等待态
this.state = 'pending'
// 终值
this.value = undefined
// 拒因
this.reason = undefined
//成功函数
let resolve = value => {
// 状态改变,只能从等待态到成功态
if (this.state === 'pending') {
// 成功函数改变状态为成功态
this.state = 'fulfilled';
// 终值就是函数参数
this.value = value
}
}
//失败函数
let reject = (reason) => {
// 状态改变,只能从等待态到失败态
if (this.state === 'pending') {
// 失败函数改变状态为失败态
this.state = 'rejected';
// 拒因就是函数参数
this.reason = reason
}
}
// 执行器抛出错误时,会执行reject,且利用catch方法捕获错误
try {
// 函数会立即调用
executor(resolve, reject)
} catch (err) {
console.log(err)
}
}
}
到这里,仅仅靠着了解Promise的基本应用就可完成一个相当简单自己手写的Promise!是不是可简单,下面就要开始深入挖掘Promise原理咯,紧张起来吧!!!
三、Promise的then方法
Promise可直接.then操作,那么就代表它是Promise类中的方法,then方法有两个参数:onFulfilled,onRejected;成功有成功的值,失败有失败的原因。 可分为两种情况:
1)当状态state为fulfilled,则执行onFulfilled,传入this.value。当状态state为rejected,则执行onRejected,传入this.reason。
2)onFulfilled,onRejected他们是函数,且必须分别在resolve,rejecte函数调用后被调用,而value或reason依次作为他们的第一个参数。
现在基本实现简单的同步代码,但是当存在异步代码时,你去调用then方法时state还是pending等待态。所以我们就需要在then调用且仍处于等待态时,将成功函数和失败函数存到各对应的数组,一旦reject或者resolve,就调用它们。
类似于发布订阅,先将then里面的两个函数储存起来,由于一个promise可以有多个then,所以将成功或者失败的函数分别存在同一个数组内。 五、Promise链式调用
Promise链式调用,就是不断地.then用来解决回调地狱;要想实现链式调用,需要我们在第一个then里返回一个promise,一般称它为promise2,而这个promise返回的值传递到下一个then中。
当我们自己在第一个then中return了一个参数(参数未知,需判断)。这个return出来的新的promise就是onFulfilled()或onRejected()的值。
判断:首先要看第一个then返回的参数是不是promise,通过函数resolvePromise来判断自己返回的参数与规定返回promise的区别,如果我们自己返回的参数是promise,那么将其结果返回到下一个then中;如果是普通值,直接返回到下一个then中。
then(onFulfilled, onRejected) {
// 首先是定下then的返回值是promise
// 而在then方法中返回的promise称为promise2
// promise2返回值不管是啥都传递到下一个then中
let promise2 = new Promise((resolve, reject) => {
if (this.state === 'fulfilled') {
// 不确定第一个then返回值是个啥,判断x与promise2
let x = onFulfilled(this.value)
// 通过resolvePromise函数
// 处理自己的返回值x与promise2的关系
resolvePromise(promise2, x, resolve, reject)
}
// 失败
if (this.state === 'rejected') {
let x = onRejected(this.reason)
resolvePromise(promise2, x, resolve, reject)
}
// 等待
if (this.state === 'pending') {
this.onResolveCallbacks.push(() => {
let x = onFulfilled(this.value)
resolvePromise(promise2, x, resolve, reject)
})
this.onRejectCallbasks.push(() => {
let x = onRejected(this.reason)
resolvePromise(promise2, x, resolve, reject)
})
}
})
return promise2
}
六、封装resolvePromise函数
resolvePromise的作用就是用来判断我们自己返回的第一个then的返回值与promise2的区别。参数由四部分:promise2,x,resolve,reject。
1)当存在特殊情况x===promise2时,会出现"循环引用"的问题
let p = new Promise(resolve => {
resolve(0);
});
var p2 = p.then(data => {
// 循环引用,自己等待自己完成,一辈子完不成
return p2;
})
2)x值类型排除:不能是null或者undefined;如果是普通值,就直接resolve(x);如果是对象或者是函数(promise),let then = x.then
if(x!=null&(typeof x==='object'||typeof x==='function')){
let then = x.then
}
3)当x是对象或者函数(默认promise),需要做的处理:
- 声明了then
- 如果取then报错,则走reject()
- 如果then是个函数,则用call执行then,第一个参数是this,后面是成功的回调和失败的回调
- 如果成功的回调还是pormise,就递归继续解析
- 成功和失败只能调用一个 所以设定一个called来防止多次调用
function resolvePromise(promise2, x, resolve, reject) {
// 循环引用错误
if (promise2 === x) {
return reject(new TypeError('Chaining cycle detected for promise'))
}
//防止多次调用
let called;
// x不是null且x是对象或者函数
if (x != null & (typeof x === 'object' || typeof x === 'function')) {
try {
let then = x.then
// 如果then是函数,就默认是promise了
if (typeof then === 'function') {
// 借助call方法让then执行,第一个参数是this,后面是成功回调和失败回调
// 成功回调与失败回调都只会执行一次
then.call(x, data => {
if (called) return
called = true
// 成功回调的结果还是promise,就递归解析
// data又是promise,将其解析
resolvePromise(promise2, data, resolve, reject)
}, err => {
if (called) return
called = true
// 失败了就将promise失败的结果向下传递
reject(err)
})
} else {
// 新声明的then不是函数,而是对象{then:"tanni"}
resolve(x)
}
} catch (e) {
if(called)return
called = true
// 这里直接取then时就已是失败,错误捕捉
reject(e)
}
}else{
resolve(x)//x是普通值
}
}
七、其它问题
then方法中onFulfilled,onRejected都是可选参数,如果他们不是函数,必须被忽略。onFulfilled返回一个普通的值,成功时直接等于 value => value;onRejected返回一个普通的值,失败时如果直接等于 value => value,则会跑到下一个then中的onFulfilled中,所以直接扔出一个错误reason => throw err。
let promise2 = new Promise((resolve, reject) => {
if (this.state === 'fulfilled') {
// 必须是异步调用
setTimeout(() => {
// 通过try来捕捉错误
try {
let x = onFulfilled(this.value)
resolvePromise(promise2, x, resolve, reject)
} catch (e) {
reject(e)
}
})
}
// 失败
if (this.state === 'rejected') {
setTimeout(() => {
try {
let x = onRejected(this.reason)
resolvePromise(promise2, x, resolve, reject)
} catch (e) {
reject(e)
}
})
}
// 等待
if (this.state === 'pending') {
this.onResolveCallbacks.push(() => {
setTimeout(() => {
try {
let x = onFulfilled(this.value)
resolvePromise(promise2, x, resolve, reject)
} catch (e) {
reject(e)
}
})
})
this.onRejectCallbasks.push(() => {
setTimeout(() => {
try {
let x = onRejected(this.reason)
resolvePromise(promise2, x, resolve, reject)
} catch (e) {
reject(e)
}
})
})
}
})
return promise2
}
将完整代码为你献上:
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 {
let then = x.then
if (typeof then === 'function') {
then.call(x,data => {
if (called) return
called = true
resolvePromise(promise2, data, resolve, reject)
}, err => {
if (called) return
called = true
reject(err)
})
} else {
// console.log()
resolve(x)
}
} catch (e) {
if (called) return
called = true
reject(e)
}
} else {
resolve(x)//x是普通值
}
}
class Promise {
constructor(executor) {
this.state = 'pending'
this.value = undefined
this.reason = undefined
this.onResolveCallbacks = []
this.onRejectCallbasks = []
//成功函数
let resolve = value => {
if (this.state === 'pending') {
this.state = 'fulfilled';
this.value = value
this.onResolveCallbacks.forEach(fn => fn())
}
}
//失败函数
let reject = (reason) => {
if (this.state === 'pending') {
this.state = 'rejected';
this.reason = reason
this.onRejectCallbasks.forEach(fn => fn())
}
}
try {
executor(resolve, reject)
} catch (err) {
console.log(err)
}
}
// then方法
then(onFulfilled, onRejected) {
// onFulfilled onRejected是可选参数,
// onFulfilled 如果不是函数就可直接忽略,返回value
onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : value => value
// onRejected 如果不是函数就可直接忽略,扔出错误
onRejected = typeof onRejected === 'function' ? onRejected : err => { throw err }
let promise2 = new Promise((resolve, reject) => {
if (this.state === 'fulfilled') {
// 必须是异步调用
setTimeout(() => {
// 通过try来捕捉错误
try {
let x = onFulfilled(this.value)
resolvePromise(promise2, x, resolve, reject)
} catch (e) {
reject(e)
}
})
}
// 失败
if (this.state === 'rejected') {
setTimeout(() => {
try {
let x = onRejected(this.reason)
resolvePromise(promise2, x, resolve, reject)
} catch (e) {
reject(e)
}
})
}
// 等待
if (this.state === 'pending') {
this.onResolveCallbacks.push(() => {
setTimeout(() => {
try {
let x = onFulfilled(this.value)
resolvePromise(promise2, x, resolve, reject)
} catch (e) {
reject(e)
}
})
})
this.onRejectCallbasks.push(() => {
setTimeout(() => {
try {
let x = onRejected(this.reason)
resolvePromise(promise2, x, resolve, reject)
} catch (e) {
reject(e)
}
})
})
}
})
return promise2
}
}
如果你能跟我理思路梳理到这里,那么恭喜你,就已经成功将promise原理搞个差不多了,当然,如果你能充分的自己手写上述Promise原理代码,那也就真的能熟练掌握Promise原理啦!
本文参考:juejin.cn/post/684490…