Skip to main content

promise 高级技巧

实际上,Promise 有许多巧妙的高级用法,其中一些在 ALOVA 请求策略库中广泛使用。

ALOVA 是一个基于 Promise 的请求策略库,旨在帮助开发者更高效地进行 HTTP 请求处理。它通过高级的 Promise 技巧,实现了请求共享、缓存、批量请求等功能,从而简化了前端开发中的数据请求管理。

下面列举出一些。

串行执行 Promise 数组

例如,有一组需要串行执行的接口:

const p1 = () => Promise.resolve(1, console.log(1))
const p2 = () => Promise.resolve(2, console.log(2))
const p3 = () => Promise.resolve(3, console.log(3))
const pList = [p1, p2, p3]

/**
* 等价实现效果如下:

Promise.resolve().then(() => {
return p1()
}).then(res => {
return p2()
}).then(res => {
return p3()
})

*/

首先想到的使用 await:

runPList(pList)

async function runPList(pList) {
for (const p of pList) {
await p()
}
}

也可以使用 Promise 语法用 then 串起来顺序执行:

runPList(pList)

function runPList(pList) {
return pList.reduce((pre, cur) => pre.then(() => cur()), Promise.resolve())
}

串行并依赖前一个 Promise 的结果

如果后一个 Promise 的入参要依赖前一个 Promise 的结果:

const p4 = res => Promise.resolve(res + 4, console.log(res + 4))
const p5 = res => Promise.resolve(res + 5, console.log(res + 5))
const p6 = res => Promise.resolve(res + 6, console.log(res + 6))
const pList2 = [p4, p5, p6]

/**
* 等价实现效果如下:

Promise.resolve(0).then(res => {
return p1(res)
}).then(res => {
return p2(res)
}).then(res => {
return p3(res)
})

*/

解决方式跟上面差不多,只是多传递了个参数,重点是要生成统一格式的返回 Promise 的函数(p4、p5、p6):

runPList2(pList2)

function runPList2(pList) {
return pList.reduce((pre, cur) => pre.then(res => cur(res)), Promise.resolve(0))
}

实现 Koa2 洋葱模型中的 Promise

洋葱模型就是让 Promise 数组中的“中间件”能顺序处理,再按相反的顺序处理:

const p7 = async next => {
console.log(7)
await next()
console.log(77)
}
const p8 = async next => {
console.log(8)
await next()
console.log(88)
}
const p9 = async next => {
console.log(9)
await next()
console.log(99)
}
const pList3 = [p7, p8, p9]

/**
* 等价实现效果如下:

new Promise(async (resolve) => {
console.log(7)
await new Promise(async (resolve) => {
console.log(8)
await new Promise(async (resolve) => {
console.log(9)
await new Promise(async (resolve) => {
resolve()
})
console.log(99)
resolve()
})
console.log(88)
resolve()
})
console.log(77)
resolve()
})

*/

/**
* 立即执行函数简写效果:

(async function p7() {
console.log(7)
await Promise.resolve((async function p8() {
console.log(8)
await Promise.resolve((async function p9() {
console.log(9)
await Promise.resolve()
console.log(99)
})())
console.log(88)
})())
console.log(77)
})()

*/

首先我们要按顺序执行 pList,借助一个计数器 nextIndex 标识下一个执行的中间件的索引:

runPList3(pList)

function runPList3(pList) {
let nextIndex = 1
pList[0](next)

function next() {
const nextMiddleware = pList[nextIndex]
if (nextMiddleware) {
nextIndex++
nextMiddleware(next)
}
}
}

当然还要处理 next 之后的后置逻辑,我们必须返回一个 Promise 才能等待处理:

runPList3(pList)

function runPList3(pList) {
let nextIndex = 1
pList[0](next)

function next() {
const nextMiddleware = pList[nextIndex]
if (nextMiddleware) {
nextIndex++
// 这里添加一个 return,使中间件函数的执行通过 Promise 从后向前连接
return nextMiddleware(next)
} else {
// 最后一个中间件的前置逻辑执行完成后,返回的 fulfilled Promise 将开始执行 next 之后的后置逻辑
return Promise.resolve()
}
}
}