732 字
4 分钟
JS延迟模式
通常,在处理多个相互依赖的 Promise 函数时,我们会使用Promise.all
来等待所有值返回以进行处理。然而,有些情况下无法使用Promise.all
,比如在事件监听程序中。那么在这种情况下,我们应该如何处理呢?
以下面的例子为例:
let valueB
function A(b) {
// ...
}
document.addEventListener('message', () => {
A(valueB)
})
async function B() {
// 包含其他异步程序
valueB = 1
}
B()
A 在一个事件回调中执行,而 B 是一个异步函数,A 函数调用需要依赖于 B 函数的结果。当触发事件时,B 的执行并不一定已经完成,因此我们需要等到 B 完成执行后才能调用 A。那么要正确调用 A,我们可以采取一种比较直接的方式,即在调用 A 之前判断 B 是否已经完成,同时在 B 中判断是否已经触发了事件从而调用 A。
let valueB
let BFinished = false // 判断 B 是否已完成执行
let AReceived = false // 判断是否已触发事件
function A(b) {
// ...
}
document.addEventListener('message', () => {
AReceived = true
if (BFinished) {
// B 已完成执行,调用 A
A(valueB)
}
})
async function B() {
// 包含其他异步程序
valueB = 1
BFinished = true
if (AReceived) {
// 事件已经触发,需要调用 A
A(valueB)
}
}
B()
这种方式需要维护的状态变多了,原本只有一个地方调用 A,现在却变成了两个,导致耦合度很高。在这个简单的示例中可能还好,但是如果代码量增加的话就会变得很复杂。那么有没有一种等待的模式呢?也就是说,在执行 A 时,如果 B 还未完成,就等待 B 完成后再执行 A。在这种情况下,Promise
函数非常适合使用。Promise
函数在没有调用resolve
时会一直保持pending
状态,而一旦调用了resolve
,pending
状态就会立即结束并返回结果。因此,我们可以利用Promise
函数来接收 B 的返回值,并在 B 中使用resolve
来结束pending
状态。
首先,我们需要创建一个延迟对象,该对象包含一个promise
、一个resolve
方法和一个reject
方法,以便我们可以在不同的地方进行分别调用。
class Defer {
promise: Promise<any>
resolve: (value: any) => void = () => {}
reject: (reason?: any) => void = () => {}
constructor() {
this.promise = new Promise((resolve, reject) => {
this.resolve = resolve
this.reject = reject
})
Object.freeze(this)
}
}
然后,我们可以利用这个延迟对象实现延迟求值。
const deferred = new Defer()
function A(b) {
// ...
}
document.addEventListener('message', async () => {
const valueB = await deferred.promise // 使用 promise 接收 B 的值
A(valueB)
})
async function B() {
// 包含其他异步程序
deferred.resolve(1) // 调用 resolve 结束 pending
}
B()
使用这种方式,逻辑更加清晰,而且不需要处理各种状态变更。