原生Promise的基本用法
const p = new Promise((resolve, reject) => {
setTimeout(() => {
resolve('我是resolve的值')
}, 200)
})
p.then((value) => {
console.log(`成功时时触发我:${value}`)
}, (error) => {
console.log(`失败时触发我:${error}`);
})
Promise是一个构造函数,使用new操作符来实例化,生成的p具有then方法,用来注册成功时回调(onFulfilled)或者失败时回调(onRejected)
动手实现
Promise传入了一个函数fn,并且在实例化的时候执行了这个fn,顺便传递了resolve
和reject
函数
function Promise(fn) {
// 用来执行成功的回调
const resolve = (value) => {
}
// 用来执行失败的回调
const reject = (error) => {
}
// 实例化直接执行初始函数
fn(resolve, reject)
}
Promise实例化以后具有一个then方法,说明then方法在原型上边,then方法用来注册成功时回调(onFulfilled)或者失败时回调(onRejected)
Promise.prototype.then = function (onFulfilled, onRejected) {
this.onFulfilled = onFulfilled
this.onRejected = onRejected
}
一旦onFulfilled和onRejected被注册成功之后,我们就可以在resolve
和reject
当中执行这两个回调
function Promise(fn) {
this.value = null
this.error = null
this.onFulfilled = null
this.onRejected = null
// 用来执行成功的回调
const resolve = (value) => {
this.value = value
this.onFulfilled(value)
}
// 用来执行失败的回调
const reject = (error) => {
this.error = error
this.onRejected(error)
}
// 实例化直接执行初始函数
fn(resolve, reject)
}
开始测试
const p = new Promise((resolve, reject) => {
setTimeout(() => {
resolve('我是resolve的值')
}, 200)
})
p.then((value) => {
console.log(`成功时时触发我:${value}`)
}, (error) => {
console.log(`失败时触发我:${error}`);
})
分析执行流程
- 先实例化得到一个p,此时p身上的value 、error 、onFulfilled 、onRejected 都还只是
null
- 这时候调用p.then()方法,传入了两个函数用于注册onFulfilled和onRejected,
- then执行完毕以后,p身上的onFulfilled 、onRejected 就变成了
函数
- 过了200毫秒,执行了resolve(),在resolve里边调用了p身上的onFulfilled
- 于是控制台输出 “成功时时触发我:我是resolve的值”
问题分析
new Promise的时候,resolve是在200毫秒以后调用的,如果是立即调用resolve会发生什么?
const p = new Promise((resolve, reject) => {
resolve('我是resolve的值')
})
p.then((value) => {
console.log(`成功时时触发我:${value}`)
}, (error) => {
console.log(`失败时触发我:${error}`);
})
我们发现,当resolve调用的时候,其实then还没开始执行,于是p身上的onFulfilled还只是null
,此时去强行执行resolve,并触发onFulfilled回调就会报错,那应该如何处理?
我们要想一个办法,保证resolve执行的时候,onFulfilled已经注册成为函数,于是可以这样
function Promise(fn) {
this.value = null
this.error = null
this.onFulfilled = null
this.onRejected = null
// 用来执行成功的回调
const resolve = (value) => {
setTimeout(() => {
this.value = value
this.onFulfilled(value)// 兼容同步任务,而把回调执行的时机推迟到宏任务当中
})
}
// 用来执行失败的回调
const reject = (error) => {
setTimeout(() => {
this.error = error
this.onRejected(error)
})
}
// 实例化直接执行初始函数
fn(resolve, reject)
}
既然resolve 执行的时候onFulfilled还是null
,那就干脆把onFulfilled执行的时间往后推迟,推迟到宏任务
队列当中去,这样就保证了,then方法先执行,onFulfilled函数后执行
可是原生Promise当中,onFulfilled不是在宏任务
当中,而是放在了微任务
里面,为的是更快地执行onFulfilled,于是修改代码,把setTimeout
改成queueMicrotask
function Promise(fn) {
this.value = null
this.error = null
this.onFulfilled = null
this.onRejected = null
// 用来执行成功的回调
const resolve = (value) => {
queueMicrotask(() => {
this.value = value
this.onFulfilled(value)// 兼容同步任务,而把回调执行的时机推迟到宏任务当中
})
}
// 用来执行失败的回调
const reject = (error) => {
queueMicrotask(() => {
this.error = error
this.onRejected(error)
})
}
// 实例化直接执行初始函数
fn(resolve, reject)
}
到这里,Promise的基本功能就出来了
进一步完善
Promise还有3种状态:‘pending’、‘fulfilled’、‘rejected’
代码中补充3种状态
function Promise(fn) {
this.value = null
this.error = null
this.onFulfilled = null
this.onRejected = null
this.status = 'pending'
// 用来执行成功的回调
const resolve = (value) => {
if (this.status === 'pending') {
queueMicrotask(() => {
this.status = 'fulfilled'
this.value = value
this.onFulfilled(value)// 兼容同步任务,而把回调执行的时机推迟到微任务当中
})
}
}
// 用来执行失败的回调
const reject = (error) => {
if (this.status === 'pending') {
queueMicrotask(() => {
this.status = 'rejected'
this.error = error
this.onRejected(error)
})
}
}
// 实例化直接执行初始函数
fn(resolve, reject)
}
// 用于注册onFulfilled和onRejected
Promise.prototype.then = function (onFulfilled, onRejected) {
if (this.status === 'pending') {
this.onFulfilled = onFulfilled
this.onRejected = onRejected
} else if (this.status === 'fulfilled') {
onFulfilled(this.value)
} else {
onRejected(this.error)
}
}
观察代码发现,Promise的状态只会从‘pending’变为‘fulfilled’,或者从‘pending’变为‘rejected’,一旦状态改变了,就不会再发生变化