据我所知,在ES7/ES2016中,在代码中放置多个await将类似于用promise链接.then(),这意味着它们将一个接一个地执行,而不是并行执行。例如,我们有以下代码:

await someCall();
await anotherCall();

我是否正确理解只有当someCall()完成时才会调用另一个otherCall()?并行调用它们最优雅的方式是什么?

我想在Node中使用它,所以也许有一个异步库的解决方案?

编辑:我对这个问题中提供的解决方案不满意:由于异步生成器中的非并行等待承诺,所以速度变慢,因为它使用生成器,我问的是一个更通用的用例。


您可以等待Promise.all():

await Promise.all([someCall(), anotherCall()]);

要存储结果:

let [someResult, anotherResult] = await Promise.all([someCall(), anotherCall()]);

请注意,Promise.all很快就会失败,这意味着一旦提供给它的一个承诺被拒绝,那么整个承诺就会被拒绝。

const happy=(v,ms)=>new Promise((resolve)=>setTimeout(()=>resolve(v),ms))const sad=(v,ms)=>new Promise((_,reject)=>setTimeout(()=>reject(v),ms))承诺。所有([快乐('快乐',100),悲伤('悲伤',50)]).then(console.log).catch(console.log)//“sad”

相反,如果您希望等待所有承诺兑现或拒绝,则可以使用Promise.allSettled。请注意,Internet Explorer本机不支持此方法。

const happy=(v,ms)=>new Promise((resolve)=>setTimeout(()=>resolve(v),ms))const sad=(v,ms)=>new Promise((_,reject)=>setTimeout(()=>reject(v),ms))承诺。一切都已解决([快乐('快乐',100),悲伤('悲伤',50)]).then(console.log)//[{“status”:“已完成”,“value”:“快乐”},{“status”:“拒绝”,“reason”:“悲伤”}]

注意:如果您使用Promise.在拒绝发生之前完成的所有操作都不会回滚,因此您可能需要注意这种情况。例如如果你有5个动作,4个快速,1个慢速和慢速拒绝。这4个操作可能已经执行,因此您可能需要回滚。在这种情况下,考虑使用Promise.allSettled,同时它将提供哪些操作失败和哪些操作未失败的确切细节。


TL;博士

对并行函数调用使用Promise.all,当发生错误时,应答行为不正确。


首先,立即执行所有异步调用并获取所有Promise对象。其次,对Promise对象使用await。这样,当您等待第一个Promise来解决其他异步调用时,其他异步调用仍在进行。总的来说,您将只等待最慢的异步调用。例如:

// Begin first call and store promise without waiting
const someResult = someCall();

// Begin second call and store promise without waiting
const anotherResult = anotherCall();

// Now we await for both results, whose async processes have already been started
const finalResult = [await someResult, await anotherResult];

// At this point all calls have been resolved
// Now when accessing someResult| anotherResult,
// you will have a value instead of a promise

JSbin示例:http://jsbin.com/xerifanima/edit?js安慰

注意:只要第一个等待调用发生在所有异步调用之后,那么等待调用是在同一条线上还是在不同的线上并不重要。见JohnnyHK的评论。


更新:根据@bergi的回答,这个答案在错误处理中的时间不同,它不会在错误发生时抛出错误,而是在所有承诺都执行后抛出错误。我将结果与@jonny的提示进行比较:[result1,result2]=Promise.all([async1(),async2()]),检查以下代码片段

常量correctAsync500ms=()=>{return new Promise(解析=>{setTimeout(resolve,500,'correct500msResult');});};常量correctAsync100ms=()=>{return new Promise(解析=>{setTimeout(resolve,100,'correct100msResult');});};常量拒绝同步100ms=()=>{return new Promise((resolve,reject)=>{setTimeout(拒绝,100,'拒绝100毫秒错误');});};const asyncInArray=异步(fun1,fun2)=>{const label='测试数组中的异步函数';尝试{console.time(标签);常量p1=fun1();常量p2=fun2();const result=[await p1,wait p2];console.timeEnd(标签);}捕获(e){console.error('错误为',e);console.timeEnd(标签);}};常量asyncInPromiseAll=async(fun1,fun2)=>{const label='使用Promise.all测试异步函数';尝试{console.time(标签);let[value1,value2]=等待Promise。all([fun1(),fun2()]);console.timeEnd(标签);}捕获(e){console.error('错误为',e);console.timeEnd(标签);}};(异步()=>{console.group('无错误的同步功能');console.log('无错误的同步函数:启动')等待异步InArray(correctAsync500ms,correctAsync 100ms);等待异步InPromiseAll(correctAsync500ms,correctAsync 100ms);console.groupEnd();console.group('带错误的同步功能');console.log('带有错误的同步函数:start')等待异步InArray(correctAsync500ms,rejectAsync100ms);等待异步InPromiseAll(correctAsync500ms,rejectAsync100ms);console.groupEnd();})();


我投票支持:

await Promise.all([someCall(), anotherCall()]);

请注意,调用函数时,可能会导致意外结果:

// Supposing anotherCall() will trigger a request to create a new User

if (callFirst) {
  await someCall();
} else {
  await Promise.all([someCall(), anotherCall()]); // --> create new User here
}

但以下始终会触发创建新用户的请求

// Supposing anotherCall() will trigger a request to create a new User

const someResult = someCall();
const anotherResult = anotherCall(); // ->> This always creates new User

if (callFirst) {
  await someCall();
} else {
  const finalResult = [await someResult, await anotherResult]
}

更新:

最初的答案使正确处理拒绝承诺变得困难(在某些情况下是不可能的)。正确的解决方案是使用Promise.all:

const [someResult, anotherResult] = await Promise.all([someCall(), anotherCall()]);

原答覆:

只需确保在等待其中一个函数之前调用这两个函数:

// Call both functions
const somePromise = someCall();
const anotherPromise = anotherCall();

// Await both promises    
const someResult = await somePromise;
const anotherResult = await anotherPromise;

我创建了一个助手函数waitAll,也许它可以让它更甜。它目前只在nodejs中工作,在浏览器chrome中不工作。

    //const parallel = async (...items) => {
    const waitAll = async (...items) => {
        //this function does start execution the functions
        //the execution has been started before running this code here
        //instead it collects of the result of execution of the functions

        const temp = [];
        for (const item of items) {
            //this is not
            //temp.push(await item())
            //it does wait for the result in series (not in parallel), but
            //it doesn't affect the parallel execution of those functions
            //because they haven started earlier
            temp.push(await item);
        }
        return temp;
    };

    //the async functions are executed in parallel before passed
    //in the waitAll function

    //const finalResult = await waitAll(someResult(), anotherResult());
    //const finalResult = await parallel(someResult(), anotherResult());
    //or
    const [result1, result2] = await waitAll(someResult(), anotherResult());
    //const [result1, result2] = await parallel(someResult(), anotherResult());


我已经创建了一个要点,测试了一些解决承诺的不同方法,并给出了结果。查看可行的选项可能会有所帮助。

编辑:根据Jin Lee的评论提供内容

// Simple gist to test parallel promise resolution when using async / await

function promiseWait(time) {
  return new Promise((resolve, reject) => {
    setTimeout(() => {
        resolve(true);
    }, time);
});
}


async function test() {
    return [
    await promiseWait(1000),
    await promiseWait(5000),
    await promiseWait(9000),
    await promiseWait(3000),
    ]
}

async function test2() {
    return {
        'aa': await promiseWait(1000),
        'bb': await promiseWait(5000),
        'cc': await promiseWait(9000),
        'dd': await promiseWait(3000),
    }
}

async function test3() {
    return await {
        'aa': promiseWait(1000),
        'bb': promiseWait(5000),
        'cc': promiseWait(9000),
        'dd': promiseWait(3000),
    }
}

async function test4() {
    const p1 =  promiseWait(1000);
    const p2 =  promiseWait(5000);
    const p3 =  promiseWait(9000);
    const p4 =  promiseWait(3000);
    return {
        'aa': await p1,
        'bb': await p2,
        'cc': await p3,
        'dd': await p4,
    };
}

async function test5() {
    return await Promise.all([
                             await promiseWait(1000),
                             await promiseWait(5000),
                             await promiseWait(9000),
                             await promiseWait(3000),
                             ]);
}

async function test6() {
    return await Promise.all([
                             promiseWait(1000),
                             promiseWait(5000),
                             promiseWait(9000),
                             promiseWait(3000),
                             ]);
}

async function test7() {
    const p1 =  promiseWait(1000);
    const p2 =  promiseWait(5000);
    const p3 =  promiseWait(9000);
    return {
        'aa': await p1,
        'bb': await p2,
        'cc': await p3,
        'dd': await promiseWait(3000),
    };
}

let start = Date.now();

test().then((res) => {
    console.log('Test Done, elapsed', (Date.now() - start) / 1000, res);

    start = Date.now();
    test2().then((res) => {
        console.log('Test2 Done, elapsed', (Date.now() - start) / 1000, res);

        start = Date.now();
        test3().then((res) => {
            console.log('Test3 Done, elapsed', (Date.now() - start) / 1000, res);

            start = Date.now();
            test4().then((res) => {
                console.log('Test4 Done, elapsed', (Date.now() - start) / 1000, res);

                start = Date.now();
                test5().then((res) => {
                    console.log('Test5 Done, elapsed', (Date.now() - start) / 1000, res);

                    start = Date.now();
                    test6().then((res) => {
                        console.log('Test6 Done, elapsed', (Date.now() - start) / 1000, res);
                    });

                    start = Date.now();
                    test7().then((res) => {
                        console.log('Test7 Done, elapsed', (Date.now() - start) / 1000, res);
                    });
                });
            });

        });
    });

});
/*
Test Done, elapsed 18.006 [ true, true, true, true ]
Test2 Done, elapsed 18.009 { aa: true, bb: true, cc: true, dd: true }
Test3 Done, elapsed 0 { aa: Promise { <pending> },
  bb: Promise { <pending> },
  cc: Promise { <pending> },
  dd: Promise { <pending> } }
Test4 Done, elapsed 9 { aa: true, bb: true, cc: true, dd: true }
Test5 Done, elapsed 18.008 [ true, true, true, true ]
Test6 Done, elapsed 9.003 [ true, true, true, true ]
Test7 Done, elapsed 12.007 { aa: true, bb: true, cc: true, dd: true }
*/

没有Promise.all()还有另一种方法可以并行执行:

首先,我们有两个功能来打印数字:

function printNumber1() {
   return new Promise((resolve,reject) => {
      setTimeout(() => {
      console.log("Number1 is done");
      resolve(10);
      },1000);
   });
}

function printNumber2() {
   return new Promise((resolve,reject) => {
      setTimeout(() => {
      console.log("Number2 is done");
      resolve(20);
      },500);
   });
}

这是顺序的:

async function oneByOne() {
   const number1 = await printNumber1();
   const number2 = await printNumber2();
} 
//Output: Number1 is done, Number2 is done

这是平行的:

async function inParallel() {
   const promise1 = printNumber1();
   const promise2 = printNumber2();
   const number1 = await promise1;
   const number2 = await promise2;
}
//Output: Number2 is done, Number1 is done

    // A generic test function that can be configured 
    // with an arbitrary delay and to either resolve or reject
    const test = (delay, resolveSuccessfully) => new Promise((resolve, reject) => setTimeout(() => {
        console.log(`Done ${ delay }`);
        resolveSuccessfully ? resolve(`Resolved ${ delay }`) : reject(`Reject ${ delay }`)
    }, delay));

    // Our async handler function
    const handler = async () => {
        // Promise 1 runs first, but resolves last
        const p1 = test(10000, true);
        // Promise 2 run second, and also resolves
        const p2 = test(5000, true);
        // Promise 3 runs last, but completes first (with a rejection) 
        // Note the catch to trap the error immediately
        const p3 = test(1000, false).catch(e => console.log(e));
        // Await all in parallel
        const r = await Promise.all([p1, p2, p3]);
        // Display the results
        console.log(r);
    };

    // Run the handler
    handler();
    /*
    Done 1000
    Reject 1000
    Done 5000
    Done 10000
    */

虽然设置p1、p2和p3并不是严格地并行运行它们,但它们不会阻碍任何执行,您可以通过捕获捕获上下文错误。


在我的例子中,我有几个任务要并行执行,但我需要对这些任务的结果做一些不同的事情。

function wait(ms, data) {
    console.log('Starting task:', data, ms);
    return new Promise(resolve => setTimeout(resolve, ms, data));
}

var tasks = [
    async () => {
        var result = await wait(1000, 'moose');
        // do something with result
        console.log(result);
    },
    async () => {
        var result = await wait(500, 'taco');
        // do something with result
        console.log(result);
    },
    async () => {
        var result = await wait(5000, 'burp');
        // do something with result
        console.log(result);
    }
]

await Promise.all(tasks.map(p => p()));
console.log('done');

输出:

Starting task: moose 1000
Starting task: taco 500
Starting task: burp 5000
taco
moose
burp
done

(异步函数(){功能等待(毫秒,数据){console.log('启动任务:',数据,毫秒);return new Promise(resol=>setTimeout(resolve,ms,data));}var任务=[异步()=>{var result=等待等待(1000,'moose');//做些有结果的事console.log(结果);},异步()=>{var result=等待等待(500,'taco');//做些有结果的事console.log(结果);},异步()=>{var result=等待等待(5000,'burp');//做些有结果的事console.log(结果);}]await Promise.all(tasks.map(p=>p()));console.log(“one”);})();


await Promise.all([someCall(),anotherCall()]);正如已经提到的那样,它将充当线程围栏(在CUDA这样的并行代码中非常常见),因此它将允许其中的所有承诺在不相互阻止的情况下运行,但将阻止执行继续,直到解决所有问题。

另一种值得分享的方法是Node.js异步,如果任务直接与API调用、I/O操作等有限资源的使用相关联,它还允许您轻松控制通常需要的并发量。

// create a queue object with concurrency 2
var q = async.queue(function(task, callback) {
  console.log('Hello ' + task.name);
  callback();
}, 2);

// assign a callback
q.drain = function() {
  console.log('All items have been processed');
};

// add some items to the queue
q.push({name: 'foo'}, function(err) {
  console.log('Finished processing foo');
});

q.push({name: 'bar'}, function (err) {
  console.log('Finished processing bar');
});

// add some items to the queue (batch-wise)
q.push([{name: 'baz'},{name: 'bay'},{name: 'bax'}], function(err) {
  console.log('Finished processing item');
});

// add some items to the front of the queue
q.unshift({name: 'bar'}, function (err) {
  console.log('Finished processing bar');
});

Medium文章自动收录(阅读更多)


您可以调用多个异步函数,而无需等待它们。这将并行执行它们。执行此操作时,将返回的Promise保存在变量中,并在某个时刻单独或使用Promise.all()等待它们,然后处理结果。

还可以使用try…包装函数调用。。。catch处理单个异步操作的失败并提供回退逻辑。

下面是一个示例:观察日志,在单个异步函数开始执行时打印的日志会立即打印,即使第一个函数需要5秒才能解析。

函数someLongFunc(){return new Promise((resolve,reject)=>{console.log('执行功能1')setTimeout(分辨率,5000)})}函数另一个LongFunc(){return new Promise((resolve,reject)=>{console.log('执行功能2')setTimeout(分辨率,5000)})}异步函数main(){让某个LongFuncPromise,另一个LongFunc Promiseconst start=Date.now()尝试{someLongFuncPromise=someLongFunc()}捕获(ex){console.error('功能1期间出现问题')}尝试{anotherLongFuncPromise=另一个LongFunc()}捕获(ex){console.error('功能2期间出现问题')}等待某人LongFuncPromise等待另一个LongFuncPromiseconst totalTime=Date.now()-开始console.log('执行完成时间',totalTime)}main()


这可以通过Promise.allSettled()实现,它类似于Promise.all(),但没有快速失败行为。

async function Promise1() {
    throw "Failure!";
}

async function Promise2() {
    return "Success!";
}

const [Promise1Result, Promise2Result] = await Promise.allSettled([Promise1(), Promise2()]);

console.log(Promise1Result); // {status: "rejected", reason: "Failure!"}
console.log(Promise2Result); // {status: "fulfilled", value: "Success!"}

注意:这是一个边缘功能,浏览器支持有限,因此我强烈建议为该功能添加polyfill。