在Promise.race中完成哪些承诺

Jon*_*ych 12 javascript asynchronous async-await es6-promise

上下文:我需要进行大量可并行化的异步调用(想想大约300到3000个ajax调用).但是,我不想通过立即调用它们来拉紧浏览器或服务器.我也不想按顺序运行它们,因为它需要很长时间才能完成.我决定一次运行五个左右,并导出此功能:

async function asyncLoop(asyncFns, concurrent = 5) {
    // queue up simultaneous calls 
    let queue = [];
    for (let fn of asyncFns) {
        // fire the async function and add its promise to the queue
        queue.push(fn());
        // if max concurrent, wait for the oldest one to finish
        if (queue.length >= concurrent) {
            await queue.shift();
        }
    }
    // wait for the rest of the calls to finish
    await Promise.all(queue);
};
Run Code Online (Sandbox Code Playgroud)

其中asyncFns是一个可迭代的(尚未调用的)异步函数.

问题:这是有效的,但我发现,最老的是第一个完成的并不总是如此.我想修改函数,以便它使用Promise.race等到第一个promise成功,然后从那里继续.但是,我不知道要删除哪个承诺:

        // if max concurrent, wait for the first one to finish
        if (queue.length >= concurrent) {
            await Promise.race(queue);
            // ??? get race's completed promise
            // queue.splice(queue.indexOf(completed), 1);
        }
Run Code Online (Sandbox Code Playgroud)

如果我只知道完成哪一个的索引,我可以将它拼接出队列(我现在更像是一个集合).看起来我不能从竞赛归来的派生者那里得到最初的承诺.建议?

Raf*_*ier 7

“从队列中删除”步骤应该由已完成的承诺本身(使用then)而不是依赖于从 返回的承诺发生Promise.race。看来这是唯一的办法了。

async function asyncLoop(asyncFns, concurrent = 5) {
    // queue up simultaneous calls 
    let queue = [];
    let ret = [];
    for (let fn of asyncFns) {
        // fire the async function, add its promise to the queue, and remove
        // it from queue when complete
        const p = fn().then(res => {
            queue.splice(queue.indexOf(p), 1);
            return res;
        });
        queue.push(p);
        ret.push(p);
        // if max concurrent, wait for one to finish
        if (queue.length >= concurrent) {
            await Promise.race(queue);
        }
    }
    // wait for the rest of the calls to finish
    await Promise.all(queue);
};
Run Code Online (Sandbox Code Playgroud)

Npm 模块:https : //github.com/rxaviers/async-pool


Jon*_*ych 5

感谢@Dan D.,他在发布后不久删除了答案:

let [completed] = await Promise.race(queue.map(p => p.then(res => [p])));
Run Code Online (Sandbox Code Playgroud)

这将为队列中的每个元素创建一个承诺,当承诺完成时,将返回承诺。然后,通过竞速,您将获得首先完成的承诺。

最初在completed或周围没有括号p。由于p是一个诺言并具有一种then方法,因此该诺言再次被链接起来,返回了诺言的已解决价值,而不是诺言(因此不起作用)。我认为这就是删除答案的原因。通过将promise包装在数组中,然后使用Array Destructuring分配,可以防止其再次链接,从而获得promise。