深入理解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