JavaScript 异步编程原理
Promise
基础版
class Asuna {
constructor(executor) {
this._resolveQueue = []
this._rejectQueue = []
// 箭头函数固定this指向, 否则找不到this._resolveQueue
const _resolve = (val) => {
this._resolveQueue.forEach(callback => callback(val))
}
const _reject = (val) => {
this._rejectQueue.forEach(callback => callback(val))
}
// new Promise()时立即执行executor,并传入resolve和reject
executor(_resolve, _reject)
}
then(resolveFn, rejectFn) {
this._resolveQueue.push(resolveFn)
this._rejectQueue.push(rejectFn)
}
}
Promise 的状态
Promise本质是一个状态机,且状态只能为以下三种:
Pending(等待态)
、Fulfilled(执行态)
、`Rejected(拒绝态)状态的变更是单向的,只能从Pending -> Fulfilled 或 Pending -> Rejected,状态变更不可逆
// 仅有的三种状态
const PENDING = 'pending'
const FULFILLED = 'fulfilled'
const REJECTED = 'rejected'
class Asuna {
constructor(executor) {
// 初始化为 PENDING
this._status = PENDING
this._resolveQueue = []
this._rejectQueue = []
const _resolve = (val) => {
// 状态只能由pending到fulfilled或rejected
if (this._status !== PENDING) return
// 变更状态
this._status = FULFILLED
// 这里之所以使用一个队列来储存回调,是为了实现规范要求的 "then 方法可以被同一个 promise 调用多次"
// 如果使用一个变量而非队列来储存回调,那么即使多次p1.then()也只会执行一次回调
this._resolveQueue.forEach(callback => callback(val))
}
const _reject = (val) => {
if (this._status !== PENDING) return
this._status = REJECTED
this._rejectQueue.forEach(callback => callback(val))
}
executor(_resolve, _reject)
}
then(resolveFn, rejectFn) {
this._resolveQueue.push(resolveFn)
this._rejectQueue.push(rejectFn)
}
}
链式调用
显然
.then()
需要返回一个Promise,这样才能找到then方法,所以我们会把then方法的返回值包装成Promise。.then()
的回调需要顺序执行,以上面这段代码为例,虽然中间return了一个Promise,但执行顺序仍要保证是1->2->3。我们要等待当前Promise状态变更后,再执行下一个then收集的回调,这就要求我们对then的返回值分类讨论
then(resolveFn, rejectFn) {
return new Asuna((resolve, reject) => {
const fulfilledFn = value => {
try {
// 执行当前的Promise的成功回调,并获取返回值
let x = resolveFn(value)
// 分类讨论返回值,如果是Promise,那么等待Promise状态变更,否
if (x instanceof Asuna) {
x.then(resolve, reject)
} else {
resolve(x)
}
} catch (err) {
reject(err)
}
}
this._resolveQueue.push(fulfilledFn)
const rejectedFn = error => {
try {
let x = rejectFn(error)
if (x instanceof Asuna) {
x.then(resolve, reject)
} else {
resolve(x)
}
} catch (error) {
reject(error)
}
}
this._rejectQueue.push(rejectedFn)
})
}
值穿透 & 状态已变更情况
我们已经初步完成了链式调用,但是对于 then() 方法,我们还要两个细节需要处理一下
值穿透:根据规范,如果 then() 接收的参数不是function,那么我们应该忽略它。如果没有忽略,当then()回调不为function时将会抛出异常,导致链式调用中断
处理状态为resolve/reject的情况:其实我们上边 then() 的写法是对应状态为
padding
的情况,但是有些时候,resolve/reject 在 then() 之前就被执行(比如Promise.resolve().then()
),如果这个时候还把then()回调push进resolve/reject的执行队列里,那么回调将不会被执行,因此对于状态已经变为fulfilled
或rejected
的情况,我们直接执行then回调。
then(resolveFn, rejectFn) {
// 根据规范,如果then的参数不是function,则我们需要忽略它, 让链式调用继续往下执行
typeof resolveFn !== 'function' ? resolveFn = value => value : null
typeof rejectFn !== 'function' ? rejectFn = reason => {
throw new Error(reason instanceof Error ? reason.message : reason)
} : null
return new Asuna((resolve, reject) => {
const fulfilledFn = value => {
try {
let x = resolveFn(value)
if (x instanceof Asuna) {
x.then(resolve, reject)
} else {
resolve(x)
}
} catch (err) {
reject(err)
}
}
const rejectedFn = error => {
try {
let x = rejectFn(error)
if (x instanceof Asuna) {
x.then(resolve, reject)
} else {
resolve(x)
}
} catch (error) {
reject(error)
}
}
// 处理状态为resolve/reject的情况
if (this.status === PENDING) {
this._resolveQueue.push(fulfilledFn)
this._rejectQueue.push(rejectedFn)
} else if (this.status === FULFILLED) {
fulfilledFn(this._value)
} else if (this.status === REJECTED) {
rejectedFn(this._value)
}
})
}
← JS 继承的八种写法 JS 基础 →