在 Promises 上链接太多 .then 对性能有什么影响(如果有的话)?
What are the performance implications, if any, of chaining too many `.then`s on Promises?
我有一个 reduce 的实现,它可能会将许多 .then
处理程序链接在一起。
const reduceIterable = (fn, x0, x) => {
const iter = x[Symbol.iterator].call(x)
let cursor = iter.next()
if (cursor.done) {
throw new TypeError('reduce(...)(x); x cannot be empty')
}
let y = !isUndefined(x0) ? fn(x0, cursor.value) : (() => {
const x0 = cursor.value
cursor = iter.next()
return cursor.done ? x0 : fn(x0, cursor.value)
})()
cursor = iter.next()
while (!cursor.done) {
const { value } = cursor
y = isPromise(y) ? y.then(res => fn(res, value)) : fn(y, value)
cursor = iter.next()
}
return y
}
// you would use reduce like reduce(add, 0)([1, 2, 3]) // => 6
const reduce = (fn, x0) => {
if (!isFunction(fn)) {
throw new TypeError('reduce(x, y); x is not a function')
}
return x => {
if (isIterable(x)) return reduceIterable(fn, x0, x)
if (isAsyncIterable(x)) return reduceAsyncIterable(fn, x0, x)
if (is(Object)(x)) return reduceObject(fn, x0, x)
throw new TypeError('reduce(...)(x); x invalid')
}
}
具体来说,我正在查看 y.then(res => fn(res, value))
。因为这个 issue,这让我彻夜难眠。我知道从那时起 Promises 已经走了很长一段路,但这从未得到解决。我真的很想知道我是否可以像这样使用 Promise API,或者我是否需要做一些更难的事情。
What are the performance implications, if any, of chaining too many .then
s on Promises?
如果您比较一个 .reduce()
循环,在该循环中您将许多 .then()
链接在一起以对异步操作进行排序,并将其与异步函数内的 await
循环进行比较,其中您在开始下一个操作之前完成一个操作,主要区别在于峰值内存使用量。将许多 .then()
链接在一起将立即在内存中拥有所有 promise 对象的完整链。如果你做一个 await
循环,你一次只有一个活动的承诺。
现在,promises 不是大对象,所以即使您的链有数千个元素,它可能仍然没有 material 差异。但是,如果您想最大程度地减少峰值内存使用量,await
循环将使峰值内存使用量保持在较低水平。
至于纯粹的执行速度,故事一如既往。如果您真的关心执行速度,则必须使用 .reduce()
循环编写一个具有代表性的测试程序,并使用 await
循环编写一个生成等效输出和排序的程序,并对两者进行基准测试。性能非常依赖于特定情况,如果您真的想知道哪个更快,则必须进行测量。理论化往往是错误的,因为我们的直觉并不总是知道真正的瓶颈是什么。你得测量一下。
仅供参考,async/await
在最近的几个 nodejs 版本中已经加快了很多。
同时创建与可迭代元素一样多的 promise,这可能对通用函数不利。它完全分解为无限迭代。
您可以在检测到 promise 时跳转到异步函数:
const reduceAwait = async (fn, initial, iterable) => {
let m = await initial
for (const n of iterable) {
m = await fn(m, n)
}
return m
}
const reduceIterable = (fn, initial, iterable) => {
const iterator = iterable[Symbol.iterator]()
let m = initial
if (initial === undefined) {
const first = iter.next()
if (first.done) {
throw new TypeError('reduce of empty iterable with no initial value')
}
m = first.value
}
for (const n of iterator) {
m = fn(m, n)
if (isPromise(m)) {
return reduceAwait(fn, m, iterator)
}
}
return m
}
m = await fn(m, n)
也可以变成
m = fn(m, n)
if (isPromise(m)) {
m = await m
}
如果您希望混合输入的微任务最少。
我有一个 reduce 的实现,它可能会将许多 .then
处理程序链接在一起。
const reduceIterable = (fn, x0, x) => {
const iter = x[Symbol.iterator].call(x)
let cursor = iter.next()
if (cursor.done) {
throw new TypeError('reduce(...)(x); x cannot be empty')
}
let y = !isUndefined(x0) ? fn(x0, cursor.value) : (() => {
const x0 = cursor.value
cursor = iter.next()
return cursor.done ? x0 : fn(x0, cursor.value)
})()
cursor = iter.next()
while (!cursor.done) {
const { value } = cursor
y = isPromise(y) ? y.then(res => fn(res, value)) : fn(y, value)
cursor = iter.next()
}
return y
}
// you would use reduce like reduce(add, 0)([1, 2, 3]) // => 6
const reduce = (fn, x0) => {
if (!isFunction(fn)) {
throw new TypeError('reduce(x, y); x is not a function')
}
return x => {
if (isIterable(x)) return reduceIterable(fn, x0, x)
if (isAsyncIterable(x)) return reduceAsyncIterable(fn, x0, x)
if (is(Object)(x)) return reduceObject(fn, x0, x)
throw new TypeError('reduce(...)(x); x invalid')
}
}
具体来说,我正在查看 y.then(res => fn(res, value))
。因为这个 issue,这让我彻夜难眠。我知道从那时起 Promises 已经走了很长一段路,但这从未得到解决。我真的很想知道我是否可以像这样使用 Promise API,或者我是否需要做一些更难的事情。
What are the performance implications, if any, of chaining too many
.then
s on Promises?
如果您比较一个 .reduce()
循环,在该循环中您将许多 .then()
链接在一起以对异步操作进行排序,并将其与异步函数内的 await
循环进行比较,其中您在开始下一个操作之前完成一个操作,主要区别在于峰值内存使用量。将许多 .then()
链接在一起将立即在内存中拥有所有 promise 对象的完整链。如果你做一个 await
循环,你一次只有一个活动的承诺。
现在,promises 不是大对象,所以即使您的链有数千个元素,它可能仍然没有 material 差异。但是,如果您想最大程度地减少峰值内存使用量,await
循环将使峰值内存使用量保持在较低水平。
至于纯粹的执行速度,故事一如既往。如果您真的关心执行速度,则必须使用 .reduce()
循环编写一个具有代表性的测试程序,并使用 await
循环编写一个生成等效输出和排序的程序,并对两者进行基准测试。性能非常依赖于特定情况,如果您真的想知道哪个更快,则必须进行测量。理论化往往是错误的,因为我们的直觉并不总是知道真正的瓶颈是什么。你得测量一下。
仅供参考,async/await
在最近的几个 nodejs 版本中已经加快了很多。
同时创建与可迭代元素一样多的 promise,这可能对通用函数不利。它完全分解为无限迭代。
您可以在检测到 promise 时跳转到异步函数:
const reduceAwait = async (fn, initial, iterable) => {
let m = await initial
for (const n of iterable) {
m = await fn(m, n)
}
return m
}
const reduceIterable = (fn, initial, iterable) => {
const iterator = iterable[Symbol.iterator]()
let m = initial
if (initial === undefined) {
const first = iter.next()
if (first.done) {
throw new TypeError('reduce of empty iterable with no initial value')
}
m = first.value
}
for (const n of iterator) {
m = fn(m, n)
if (isPromise(m)) {
return reduceAwait(fn, m, iterator)
}
}
return m
}
m = await fn(m, n)
也可以变成
m = fn(m, n)
if (isPromise(m)) {
m = await m
}
如果您希望混合输入的微任务最少。