Node.js是原生的Promise.all并行或顺序处理吗?

时间:2015-06-13 21:20:29

标签: javascript node.js promise es6-promise

我想澄清这一点,因为documentation对此并不太清楚;

Q1: Promise.all(iterable)是按顺序还是并行处理所有承诺?或者,更具体地说,它是否相当于运行像

这样的链式承诺
p1.then(p2).then(p3).then(p4).then(p5)....

或者是其他类型的算法,其中所有p1p2p3p4p5等都在同一时间(并行),一旦所有解决(或一个拒绝),结果将被返回?

Q2:如果Promise.all并行运行,是否有方便的顺序运行迭代?

注意:我不想使用Q或Bluebird,而是使用所有原生ES6规范。

14 个答案:

答案 0 :(得分:202)

  

Promise.all(iterable)是否正在执行所有承诺?

不,承诺不能被执行"。他们在创建时开始他们的任务 - 他们只代表结果 - 并且甚至在将它们传递给Promise.all之前并行执行所有内容。

Promise.all只会等待多个承诺。它并不关心它们解决的顺序,或者计算是否并行运行。

  

有一种方便的方法来运行可迭代的顺序吗?

如果你已经有了你的承诺,除了Promise.all([p1, p2, p3, …])(没有序列概念)之外,你做的不多。但是如果你有一个可迭代的异步函数,你确实可以按顺序运行它们。基本上你需要从

获得
[fn1, fn2, fn3, …]

fn1().then(fn2).then(fn3).then(…)

并且解决方法是使用Array::reduce

iterable.reduce((p, fn) => p.then(fn), Promise.resolve())

答案 1 :(得分:38)

并行

await Promise.all(items.map(async item => { await fetchItem(item) }))

优点:更快。即使一个失败,也会执行所有迭代。

按顺序

for (let i = 0; i < items.length; i++) {
    await fetchItem(items[i])
}

优点:循环中的变量可以由每次迭代共享。表现得像普通的命令式同步代码。

答案 2 :(得分:9)

贝尔吉斯的回答使用Array.reduce让我走上正轨。

然而,要实际让函数返回我的承诺一个接一个地执行,我不得不添加更多的嵌套。

我的真实用例是一系列文件,由于下游的限制,我需要一个接一个地传输这些文件...

这是我最终的结果。

getAllFiles().then( (files) => {
    return files.reduce((p, theFile) => {
        return p.then(() => {
            return transferFile(theFile); //function returns a promise
        });
    }, Promise.resolve()).then(()=>{
        console.log("All files transferred");
    });
}).catch((error)=>{
    console.log(error);
});

如先前的答案所示,使用:

getAllFiles().then( (files) => {
    return files.reduce((p, theFile) => {
        return p.then(transferFile(theFile));
    }, Promise.resolve()).then(()=>{
        console.log("All files transferred");
    });
}).catch((error)=>{
    console.log(error);
});

在启动另一个文件之前没有等待传输完成,并且在第一个文件传输开始之前也发送了“所有文件传输”文本。

不确定我做错了什么,但想分享对我有用的东西。

编辑:自从我写这篇文章后,我现在明白为什么第一个版本不起作用。 then()希望函数返回一个promise。所以,你应该传递没有括号的函数名称!现在,我的函数需要一个参数,所以我需要在一个不带参数的匿名函数中包装!

答案 3 :(得分:3)

只是详细说明@ Bergi的答案(这很简洁,但很难理解;)

此代码将运行数组中的每个项目,并将下一个“then chain”添加到结尾;

function eachorder(prev,order) {
        return prev.then(function() {
          return get_order(order)
            .then(check_order)
            .then(update_order);
        });
    }
orderArray.reduce(eachorder,Promise.resolve());

希望这是有道理的。

答案 4 :(得分:3)

您还可以使用递归函数使用异步函数顺序处理迭代。例如,给定一个数组#include <iostream> template<int x> struct A{ int a; }; template<int x> struct B{ int b=3; operator A<x>(){ return {b+10}; } friend int operator+(A<x> a, int b); }; template<int x> int operator+(A<x> a, int b){ return a.a+b; } int main(){ std::cout<<(A<12>{9}+10)<<std::endl; // This works fine std::cout<<(B<12>{9}+10)<<std::endl; // Error return 0; } 来处理异步函数a

&#13;
&#13;
someAsyncFunction()
&#13;
&#13;
&#13;

答案 5 :(得分:2)

NodeJS不并行运行promise,因为它是单线程事件循环体系结构,所以可以并行运行它们。通过创建新的子进程以利用多核CPU的优势,可以并行运行事物。

Parallel Vs Concurent

实际上,Promise.all的作用是将promise函数堆叠在适当的队列中(请参阅事件循环体系结构),同时运行它们(调用P1,P2等),然后等待每个结果,然后解析Promise.all与所有承诺的结果。 Promise.all会在第一个失败的承诺上失败,除非您自己管理拒绝。

并行和并发之间的主要区别是,第一个将在完全相同的时间在单独的进程中运行不同的计算,并且它们将在该节奏中进行,而另一个将在不等待的情况下依次执行不同的计算以便先前的计算可以同时完成和进行而不会相互依赖。

最后,要回答您的问题,Promise.all既不会并行执行,也不会顺序执行,而是同时执行。

答案 6 :(得分:2)

平行

查看此示例

Num

通过运行代码,它将为所有六个promise控制台“ CALLED”,当它们被解决时,它将在超时后每6个响应同时进行控制台

答案 7 :(得分:0)

你可以通过for循环来完成。

异步函数返回承诺

async function createClient(client) {
    return await Client.create(client);
}

let clients = [client1, client2, client3];

如果您编写以下代码,则客户端将并行创建

const createdClientsArray = yield Promise.all(clients.map((client) =>
    createClient(client);
));

然后并行创建所有客户端。但如果你想按顺序创建客户端,那么你应该使用for循环

const createdClientsArray = [];
for(let i = 0; i < clients.length; i++) {
    const createdClient = yield createClient(clients[i]);
    createdClientsArray.push(createdClient);
}

然后按顺序创建所有客户端。

快乐的编码:)

答案 8 :(得分:0)

我一直在使用它来解决连续的承诺。我不确定这是否有帮助,但这就是我一直在做的事情。

async function run() {
    for (let val of arr) {
        const res = await someQuery(val)
        console.log(val)
    }
}

run().then().catch()

答案 9 :(得分:0)

这可能会回答你问题的一部分。

是的,您可以将一系列承诺返回函数链接起来,如下所示...... (这会将每个函数的结果传递给下一个函数)。你当然可以编辑它以向每个函数传递相同的参数(或没有参数)。

&#13;
&#13;
function tester1(a) {
  return new Promise(function(done) {
    setTimeout(function() {
      done(a + 1);
    }, 1000);
  })
}

function tester2(a) {
  return new Promise(function(done) {
    setTimeout(function() {
      done(a * 5);
    }, 1000);
  })
}

function promise_chain(args, list, results) {

  return new Promise(function(done, errs) {
    var fn = list.shift();
    if (results === undefined) results = [];
    if (typeof fn === 'function') {
      fn(args).then(function(result) {
        results.push(result);
        console.log(result);
        promise_chain(result, list, results).then(done);
      }, errs);
    } else {
      done(results);
    }

  });

}

promise_chain(0, [tester1, tester2, tester1, tester2, tester2]).then(console.log.bind(console), console.error.bind(console));
&#13;
&#13;
&#13;

答案 10 :(得分:0)

Bergi的回答帮助我使调用同步。我在下面添加了一个示例,在该示例中,我们在调用前一个函数之后调用了每个函数。

function func1 (param1) {
    console.log("function1 : " + param1);
}
function func2 () {
    console.log("function2");
}
function func3 (param2, param3) {
    console.log("function3 : " + param2 + ", " + param3);
}

function func4 (param4) {
    console.log("function4 : " + param4);
}
param4 = "Kate";

//adding 3 functions to array

a=[
    ()=>func1("Hi"),
    ()=>func2(),
    ()=>func3("Lindsay",param4)
  ];

//adding 4th function

a.push(()=>func4("dad"));

//below does func1().then(func2).then(func3).then(func4)

a.reduce((p, fn) => p.then(fn), Promise.resolve());

答案 11 :(得分:0)

使用 async await 可以很容易地顺序执行诺言数组:

let a = [promise1, promise2, promise3];

async function func() {
  for(let i=0; i<a.length; i++){
    await a[i]();
  }  
}

func();

注意:在上述实现中,如果一个承诺被拒绝,则其余的将不会执行。如果您希望所有的承诺都得到执行,则将await a[i]();包裹在try catch

答案 12 :(得分:0)

在尝试解决NodeJS中的问题时,我偶然发现了此页面:重新组装文件块。基本上: 我有一个文件名数组。 我需要以正确的顺序附加所有这些文件,以创建一个大文件。 我必须异步执行此操作。

Node的“ fs”模块确实提供了appendFileSync,但我不想在此操作期间阻止服务器。我想使用fs.promises模块并找到一种将这些东西链接在一起的方法。此页面上的示例对我而言并不十分有效,因为我实际上需要执行两项操作:fsPromises.read()读取文件块,fsPromises.appendFile()链接至目标文件。也许如果我对javascript更好,那么我可以使之前的答案对我有用。 ;-)

我偶然发现了这个... https://css-tricks.com/why-using-reduce-to-sequentially-resolve-promises-works/ ...而且我能够找到一个可行的解决方案。

TLDR:

/**
 * sequentially append a list of files into a specified destination file
 */
exports.append_files = function (destinationFile, arrayOfFilenames) {
    return arrayOfFilenames.reduce((previousPromise, currentFile) => {
        return previousPromise.then(() => {
            return fsPromises.readFile(currentFile).then(fileContents => {
                return fsPromises.appendFile(destinationFile, fileContents);
            });
        });
    }, Promise.resolve());
};

这是一个茉莉花单元测试:

const fsPromises = require('fs').promises;
const fsUtils = require( ... );
const TEMPDIR = 'temp';

describe("test append_files", function() {
    it('append_files should work', async function(done) {
        try {
            // setup: create some files
            await fsPromises.mkdir(TEMPDIR);
            await fsPromises.writeFile(path.join(TEMPDIR, '1'), 'one');
            await fsPromises.writeFile(path.join(TEMPDIR, '2'), 'two');
            await fsPromises.writeFile(path.join(TEMPDIR, '3'), 'three');
            await fsPromises.writeFile(path.join(TEMPDIR, '4'), 'four');
            await fsPromises.writeFile(path.join(TEMPDIR, '5'), 'five');

            const filenameArray = [];
            for (var i=1; i < 6; i++) {
                filenameArray.push(path.join(TEMPDIR, i.toString()));
            }

            const DESTFILE = path.join(TEMPDIR, 'final');
            await fsUtils.append_files(DESTFILE, filenameArray);

            // confirm "final" file exists    
            const fsStat = await fsPromises.stat(DESTFILE);
            expect(fsStat.isFile()).toBeTruthy();

            // confirm content of the "final" file
            const expectedContent = new Buffer('onetwothreefourfive', 'utf8');
            var fileContents = await fsPromises.readFile(DESTFILE);
            expect(fileContents).toEqual(expectedContent);

            done();
        }
        catch (err) {
            fail(err);
        }
        finally {
        }
    });
});

我希望它能对某人有所帮助。

答案 13 :(得分:-2)

查看此示例

Promise.all 并行工作

const { range, random, forEach, delay} = require("lodash");  
const run = id => {
    console.log(`Start Task ${id}`);
    let prom = new Promise((resolve, reject) => {
        delay(() => {
            console.log(`Finish Task ${id}`);
            resolve(id);
        }, random(2000, 15000));
    });
    return prom;
}


const exec = () => {
    let proms = []; 
    forEach(range(1,10), (id,index) => {
        proms.push(run(id));
    });
    let allPromis = Promise.all(proms); 
    allPromis.then(
        res => { 
            forEach(res, v => console.log(v));
        }
    );
}

exec();