如何在数组方法链中展开 Promise 数组?
Posted
技术标签:
【中文标题】如何在数组方法链中展开 Promise 数组?【英文标题】:How Can I Unwrap an Array of Promises within a Chain of Array Methods? 【发布时间】:2020-11-06 09:38:31 【问题描述】:我有几个映射链,其中一个需要对每个数组元素执行数据库操作,所以我使用的是异步等待。
const resultsAsPromises = arr
.map(syncDouble)
.map(asyncMapper)
如果它是链中的最后一项,这不是问题,因为我可以用 Promise.all
解开它
console.log('results', await Promise.all(resultsAsPromises))
但是,我还需要在之后执行其他同步操作,所以我希望在继续下一个 .map
之前解开 Promise 的值。
有没有办法做到这一点?我想也许只是制作一个提取映射器,比如
function extractPromiseValues(value)
return value.then(v => v);
会起作用,但可惜不行。
var arr = [1, 2, 3, 4, 5];
function timeout(i)
return new Promise((resolve) =>
setTimeout(() =>
return resolve(`number is $i`);
, 1);
);
function syncDouble(i)
return i * 2;
async function asyncMapper(i)
return await timeout(i)
function extractPromiseValues(value)
return value.then(v => v);
async function main()
const resultsAsPromises = arr
.map(syncDouble)
.map(asyncMapper)
// .map(extractPromiseValues)
console.log('results', await Promise.all(resultsAsPromises))
main();
如何在数组方法链中解开一组 Promise
【问题讨论】:
.then(v => v)
本质上是一个无操作(除了为每个承诺解决方案添加另一个延迟微滴)。
我认为问题在于是否有可能有一个 .map()
调用链,在某些时候,一个返回一个承诺数组,下一个返回一个值数组,这些值从那些承诺。在这种情况下,我不确定下面的答案是否解决了这个问题。想法?
意思是,arr.map(returnValAfter1sec).map(doubleValAfterThat)
可以写成吗? (其中returnValAfter1sec
接受一个值并返回一个承诺,doubleValAfterThat
接受一个承诺并返回一个值)。
@GhassenLouhaichi 我相信我的编辑已经解决了您的解释。你同意吗?
@Sphinx 不,你不能,因为.reduce()
方法的返回值仍然是一个承诺。无论您采用何种方法,都必须在链的该部分之前插入 await
。
【参考方案1】:
与其将标识函数传递给.then()
,不如传递您的同步操作,或者在将async
函数中的承诺await
传递给您的同步操作之前:
function syncCapitalize(s)
return s.slice(0, 1).toUpperCase() + s.slice(1);
const resultsAsPromises = arr
.map(syncDouble)
.map(asyncMapper)
.map(p => p.then(syncCapitalize)); // OR
//.map(async p => syncCapitalize(await p));
在你的例子中,这看起来像:
function timeout(i)
return new Promise(resolve =>
setTimeout(() =>
resolve(`number is $i`);
, 1);
);
function syncDouble(i)
return i * 2;
function asyncMapper(i)
return timeout(i);
function syncCapitalize(s)
return s.slice(0, 1).toUpperCase() + s.slice(1);
async function main()
const arr = [1, 2, 3, 4, 5];
const resultsAsPromises = arr
.map(syncDouble)
.map(asyncMapper)
.map(p => p.then(syncCapitalize)); // OR
//.map(async p => syncCapitalize(await p));
console.log('results', await Promise.all(resultsAsPromises));
main();
或者,如果我们将问题解释为Ghassen Louhaichi has,您可以使用TC39 pipeline operator (|>
) proposal 使用以下选项之一编写链:
F# Pipelines Proposal
const results = arr
.map(syncDouble)
.map(asyncMapper)
|> Promise.all
|> await
.map(syncCapitalize);
Smart Pipelines Proposal
const results = (arr
.map(syncDouble)
.map(asyncMapper)
|> await Promise.all(#))
.map(syncCapitalize);
不幸的是,除非您使用 Babel plugin,或者在其中一个提案被合并到官方 ECMAScript 规范中之前,您必须使用 await Promise.all(...)
包装链:
const results = (await Promise.all(arr
.map(syncDouble)
.map(asyncMapper)))
.map(syncCapitalize);
最后,在您的完整示例的上下文中:
function timeout(i)
return new Promise(resolve =>
setTimeout(() =>
resolve(`number is $i`);
, 1);
);
function syncDouble(i)
return i * 2;
function asyncMapper(i)
return timeout(i);
function syncCapitalize(s)
return s.slice(0, 1).toUpperCase() + s.slice(1);
async function main()
const arr = [1, 2, 3, 4, 5];
const results = (await Promise.all(arr
.map(syncDouble)
.map(asyncMapper)))
.map(syncCapitalize);
console.log('results', results);
main();
【讨论】:
精美!我们可以在某个地方对这些提案进行投票吗? 为了学究气,在你上次sn-p的最后,你只需要console.log('results', resultsAsPromises);
,因为结果不再是promise了,事实上,数组的名字是有误导性的.
@GhassenLouhaichi 本身没有“投票”的地方,但在GitHub issues section 中进行了很多积极的辩论,以解决用例、语法、语义、实现细节等。一般来说,一种风格得到的关注越多,委员会就越有可能选择它而不是另一种。不过,请确保您不要发布多余的问题来获得关注。
@GhassenLouhaichi 对,谢谢,我错过了。复制粘贴错误。
我觉得这真的很好。 .map(p => p.then(syncCapitalize))
正是我想要的!我将不得不深入了解这个提议。包裹在Promise.all
中也一点也不差,也许就像要采取额外的步骤一样,但仍然很好。非常感谢帕特里克。以上是关于如何在数组方法链中展开 Promise 数组?的主要内容,如果未能解决你的问题,请参考以下文章
将 JQuery Promise 数组转换为数组的 JQuery Promise 的最简洁方法是啥?