如何在列表上映射异步函数? [英] How can a map an asynchronous function over a list?

查看:94
本文介绍了如何在列表上映射异步函数?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

很明显,给定一个列表 l 和一个返回承诺的函数 f ,我可以这样做:

  Promise.all(l.map(f)); 

困难的部分是,我需要按顺序映射每个元素, 。也就是说,第一个元素的映射必须在下一个元素开始之前解决。我想防止任何并行性。



我有一个方法,我将给出一个答案,但是我不确定这是一个好答案。 / p>

编辑:有些人给人的印象是,由于Javascript本身是单线程的,因此Javascript中不可能实现并行性。



请考虑以下代码:

  const delay = t =>新的Promise(resolve => setTimeout(resolve,t)); 
mapAsync([3000,2000,1000],delay).then(n => console.log(’beep:’+ n));

天真的实现 mapAsync()导致哔哔声每秒打印一次,持续三秒钟-数字按升序排列-但是正确提示会在六秒钟内逐渐发出哔哔声,数字递减



对于一个更实际的例子,想象一下一个调用 fetch()并在数组上被调用的函数

进一步编辑:



Somebody 不相信我,所以这里是小提琴。

解决方案

不能单独使用 map(),因为您应该能够处理前一个承诺的解决方案。有一个很好的使用 reduce的示例()用于对Google文章中的Promises进行排序。



reduce() 允许您链将当前项目的Promise与上一个项目的Promise链接。要启动链,请将已解决的Promise作为初始值传递给 reduce()



假定 l 作为输入数据,而 async()作为异步修改数据。它将输入数据乘以10。

  var l = [1,2,3,4]; 

函数async(data){
console.log( call with,data);
return new Promise((resolve,reject)=> {
setTimeout(()=> {console.log( resolve,data); resolve(data * 10);},1000 );
});
}

这是相关代码(其功能已内联注释)

  //将入站数据简化为Promise链
l.reduce(function(sequencePromise,inValue){
/ *对于第一个项目sequencePromise将使用
的值进行解析*传递给reduce()的Promise.resolve()调用,对于所有其他项目,它是
*处理程序返回的前一个诺言
* /
返回sequencePromise.then(function(responseValues){
/ * responseValues是一个数组,最初是传递给
的空值* reduce( ),对于后续调用来说就是concat()的创建结果。
*
*使用当前inValue调用异步。
* /
返回async(inValue).then(((outValue )=> {
/ *并将outValue连接到
*序列数组并返回它。下一项将接收到新的
*数组作为sequencePromise解析程序的值。
* /
返回responseValues.concat([outValue]);
});
});
},Promise.resolve([])/ *以已解决的Promise * /开头。然后(函数(responseValues){
console.log(responseValues);
});

控制台将最终登录

  Array [10,20,30,40] 


Obviously, given a list l and an function f that returns a promise, I could do this:

Promise.all(l.map(f));

The hard part is, I need to map each element, in order. That is, the mapping of the first element must be resolved before the the next one is even started. I want to prevent any parallelism.

I have an idea how to do this, which I will give as an answer, but I am not sure it's a good answer.

Edit: some people are under the impression that since Javascript is itself single-threaded, parallelism is not possible in Javascript.

Consider the following code:

const delay = t => new Promise(resolve => setTimeout(resolve, t));
mapAsync([3000, 2000, 1000], delay).then(n => console.log('beep: ' + n));

A naïve implementation of mapAsync() would cause "beep" to be printed out once a second for three seconds -- with the numbers in ascending order -- but a correct one would space the beeps out increasingly over six seconds, with the number in descending orders.

For a more practical example, imagine a function that invoked fetch() and was called on an array of thousands of elements.

Further Edit:

Somebody didn't believe me, so here is the Fiddle.

解决方案

You can't use map() on its own since you should be able to handle the resolution of the previous Promise. There was a good example of using reduce() for sequencing Promises in an Google article.

reduce() allowes you to "chain" the Promise of the current item with the Promise of the previous item. To start the chain, you pass a resolved Promise as initial value to reduce().

Assume l as the input data and async() to modify the data asynchronously. It will just multiply the input data by 10.

var l = [1, 2, 3 ,4];

function async(data) {
    console.log("call with ", data);
    return new Promise((resolve, reject) => {
        setTimeout(() => { console.log("resolve", data); resolve(data * 10); }, 1000);
    });
}

This is the relevant code (its function is inline-commented)

// Reduce the inout data into a Promise chain
l.reduce(function(sequencePromise, inValue) {
    /* For the first item sequencePromise will resolve with the value of the 
     * Promise.resolve() call passed to reduce(), for all other items it's 
     * the previous promise that was returned by the handler in the next line.
     */
    return sequencePromise.then(function(responseValues) {
        /* responseValues is an array, initially it's the empty value passed to
         * reduce(), for subsequent calls it's the concat()enation result.
         *
         * Call async with the current inValue.
         */
        return async(inValue).then((outValue) => {
            /* and concat the outValue to the 
             * sequence array and return it. The next item will receive that new 
             * array as value to the resolver of sequencePromise.
             */
            return responseValues.concat([outValue]);
        });
    });
}, Promise.resolve([]) /* Start with a resolved Promise */ ).then(function(responseValues){
    console.log(responseValues);
});

The console will finally log

Array [ 10, 20, 30, 40 ]

这篇关于如何在列表上映射异步函数?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆