深入理解JavaScript Promise:从异步问题到实现原理
前言
由于JavaScript是单线程的,这就意味着在任何时间点,JacaScript只能执行一个任务。所以这就会存在异步的问题,对于V8来说,它会优先执行同步代码,再执行异步代码。所以我们应该如何处理异步代码呢?接下来在本文中Virtual09将会解析一种解决异步代码的操作——Promise。
正文
解读Promise
我们先来看一个未处理的简单异步
function a(){
setTimeout(()=>{
console.log('a is ok');
},1000)
}
function b(){
console.log('b is ok');
}
a()
b()
我们在a()中添加一个定时器,让a()先等待一秒再执行,但是b()不需要花时间等待,可以执行完,所以V8会优先把b()执行完之后才会等待a()的一秒等待完再执行。那么我们使用Promise如何解决呢?这里我们需要的是先打印a(),但是a()是会触发异步的,所以我们在a()中创建一个Promise对象,并retrun返回这个实例对象,new Promise((resolve,reject)=>{})实例对象接收一个回调函数,回调函数中接收两个参数,我们需要注意的是,这两个参数其实也是函数,调用不同的参数,效果会不一样。
function a(){
return new Promise((resolve,reject)=>{
setTimeout(()=>{
console.log('a is ok');
resolve()
},1000)
})
}
function b(){
console.log('b is ok');
}
a().then(()=>{
b()
}).catch(err=>{
console.log(err);
})
这里我们需要注意的是假设我们不调用resolve,这个.then就走不下去了,至于是为什么,等下再给各位讲解一番。调用resolve就会触发.then,调用reject就会触发.catch用于捕获代码报错。

从实现基本原理再看看Promise
从实现原理看Promise我们要注意Promise拥有三种状态:pending、fulfilled(resolved)、rejected,分别是,当我们实例化一个Promise对象时,会的带一个状态为pending的实例对象,调用resolve时,实例对象的状态就会变成fulfilled(resolved),同理调用reject实例对象的状态就会变成rejected,也是就是说当该对象中的状态没有变更为fulfilled,then接受到的回调是不会触发的,同理该对象中的状态没有变更为rejected,catch接受到的回调也不会触发.
Promise 构造函数接受一个执行器函数作为参数,这个函数接收两个参数:resolve 和 reject,它们都是函数。resolve 用于当异步操作成功时调用,reject 则用于操作失败时调用。
class MyPromise{
//构造函数
constructor(executor){
executor(resolve,reject)
}
}
前面我们说到了Promise有三种状态,并且创建一个实例化对象时,状态为pending,所以我们使用this.state = 'pending',在Promise构造函数中的this是指向实例对象.resolve和reject函数调用时可以接受一个参数的,所以我们也需要为其准备参数。我们再来思考一个问题

假设我们这里的resolve不调用,也是说.then里面的回调函数就不会触发,,但是一旦当我们调用了resolve,.then就需要触发回调函数对吧,所以我们必须把.then里面的回调函数存在来,而且.then后面还一个接.then也就是说会有多个回调函数,所以我们用一个数组来存回调函数。当调用resolve时,我们就把数组中的回调函数触发掉。
class MyPromise{
//构造函数
constructor(executor){
this.state = 'pending'
this.value = undefined //临时保存resolve
this.reason = undefined // 临时保存reject的参数
this.onFullfilledCallbacks = [] //用来装then中的回调
this.onRejectedCallbacks = [] //用来装catch中的回调
const resolve = (value)=>{
if(this.state === 'pending'){
this.state = 'fulfilled'
this.value = value
this.onFullfilledCallbacks.forEach(cb=>cb(value))
}
}
const reject = (reason)=>{
if(this.state === 'pending'){
this.state = 'rejected'
this.reason = reason
this.onRejectedCallbacks.forEach(ct=>ct(reason))
}
}
// 构造函数接受一个形参函数,执行调形参,并给它两个实参resolve和reject
executor(resolve,reject)
}
}
现在我们就差.then部分的实现了,其实.then是可以接收两个函数的,接收的第二个函数就是等同于reject.首先我们必须判断一下.then接收的是否是两个函数
onFulfilled = typeof onFulfilled === 'function'
? onFulfilled : value =>value;
onRejected = typeof onRejected === 'function'
? onRejected :reason =>{throw reason};
对于onFulfilled,我们先判断它是不是一个函数,如果不是它就会被替代成一个简单的箭头函数。而onRejected会直接抛出错误。
现在我们需要对于实例对象的状态进行一个判断,有三种情况,一:实例对象本身状态就是fullfilled,也就是then前面的那个哥们已经执行完了,所以我们需要自己把.then执行完,而且.then本身是一个异步,还是微任务的一个函数,但是模拟微任务难度有点大,我们就使用setTime()来模拟就好了
if(this.state === 'fullfilled'){
setTimeout(()=>{
try {
const res = onFulfilled(this.value)
resolve(res)
} catch (error) {
reject(error)
}
})
}
this.value是因为回调函数会接受参数,所以我们这里也需要接受参数,本身也是说resolve自己也会接受参数,假设resolve自己接收了一个参数,并自己先执行完成了,我们的this.value=value,会把resolve的参数赋值给this.value,所以不用担心this.value为空。同理,如果实例对象的状态是rejected,我们也需要自己去执行掉catch,当实例对象的状态为pending,这个时候我们就需要把回调函数分别存入onFullfilledCallbacks,以及onRejectedCallbacks.
if(this.state === 'pending'){
this.onFullfilledCallbacks.push((value)=>{
setTimeout(()=>{
onFulfilled(value)
})
})
this.onRejectedCallbacks.push((reason)=>{
setTimeout(()=>{
onRejected(reason)
})
})
}
这样我们就基本模拟了Promise的实现原理了。
源码:
class MyPromise{
//构造函数
constructor(executor){
this.state = 'pending'
this.value = undefined //临时保存resolve
this.reason = undefined // 临时保存reject的参数
this.onFullfilledCallbacks = [] //用来装then中的回调
this.onRejectedCallbacks = [] //用来装catch中的回调
const resolve = (value)=>{
if(this.state === 'pending'){
this.state = 'fulfilled'
this.value = value
this.onFullfilledCallbacks.forEach(cb=>cb(value))
}
}
const reject = (reason)=>{
if(this.state === 'pending'){
this.state = 'rejected'
this.reason = reason
this.onRejectedCallbacks.forEach(ct=>ct(reason))
}
}
executor(resolve,reject)
}
then(onFulfilled,onRejected){
onFulfilled = typeof onFulfilled === 'function'
? onFulfilled : value =>value;
onRejected = typeof onRejected === 'function'
? onRejected :reason =>{throw reason};
return new MyPromise((resolve,reject)=>{
if(this.state === 'fullfilled'){
setTimeout(()=>{
try {
const res = onFulfilled(this.value)
resolve(res)
} catch (error) {
reject(error)
}
})
if(this.state ==='rejected'){
setTimeout(()=>{
try {
const res = onRejected(this.reason)
resolve(res)
} catch (error) {
reject(error)
}
})
}
if(this.state === 'pending'){
this.onFullfilledCallbacks.push((value)=>{
setTimeout(()=>{
onFulfilled(value)
})
})
this.onRejectedCallbacks.push((reason)=>{
setTimeout(()=>{
onRejected(reason)
})
})
}
})
}
}
那么,本文也到此结束了,感谢大家阅读,若有不足恳请指出!!
转载自:https://juejin.cn/post/7392898777056346153