如何在Javascript中并行运行async/await

NoN*_*ded 55 javascript async-await

最后async/ await将在IE以外的所有主流浏览器中得到支持.所以现在我们可以用async/ 开始编写更易读的代码了.await但是有一个问题.很多人使用异步等待这样:

const userResponse = await fetchUserAsync();
const postsResponse = await fetchPostsAsync();
Run Code Online (Sandbox Code Playgroud)

虽然这个代码是可读的,但它有一个问题,它会串行运行这些函数,在完成用户的提取之前它不会开始提取帖子.解决方案很简单,我们需要并行获取资源.

所以我想做的是(伪语言):

fn task() {
  result-1 = doAsync();
  result-2 = doAsync();
  result-n = doLongAsync();

  // handle results together
  combinedResult = handleResults(result-1, result-2);

  lastResult = handleLastResult(result-n);
}
Run Code Online (Sandbox Code Playgroud)

NoN*_*ded 106

你可以写这样的东西:

const responses = await Promise.all([
 fetchUserAsync(),
 fetchPostsAsync(),
]);

const userResponse = responses[0];
const postsResponse = responses[1];
Run Code Online (Sandbox Code Playgroud)

这很容易吗?但是有一个问题!Promise.all具有快速失败的行为,这意味着,一旦承诺被拒绝,它就会拒绝.可能你想要一个更强大的解决方案,我们负责处理任何提取的拒绝.幸运的是有一个解决方案,它可以简单地使用async/ await不需要使用Promise.all.一个工作的例子:

console.clear();

function wait(ms, data) {
  return new Promise( resolve => setTimeout(resolve.bind(this, data), ms) );
}

/** 
 * This will run in series, because 
 * we call a function and immediately wait for it's result, 
 * so this will finish in 1s.
 */
async function series() {
  return {
    result1: await wait(500, 'seriesTask1'),
    result2: await wait(500, 'seriesTask2'),
  }
}

/** 
 * While here we call the functions first,
 * then wait for the result later, so 
 * this will finish in 500ms.
 */
async function parallel() {
  const task1 = wait(500, 'parallelTask1');
  const task2 = wait(500, 'parallelTask2');

  return {
    result1: await task1,
    result2: await task2,
  }
}

async function taskRunner(fn, label) {
  const startTime = performance.now();
  console.log(`Task ${label} starting...`);
  let result = await fn();
  console.log(`Task ${label} finished in ${ Number.parseInt(performance.now() - startTime) } miliseconds with,`, result);
}

void taskRunner(series, 'series');
void taskRunner(parallel, 'parallel');


/* 
 * The result will be:
 * Task series starting...
 * Task parallel starting...
 * Task parallel finished in 500 milliseconds with, { "result1": "parallelTask1", "result2": "parallelTask2" }
 * Task series finished in 1001 milliseconds with, { "result1": "seriesTask1", "result2": "seriesTask2" }
 */
Run Code Online (Sandbox Code Playgroud)

注意:您需要一个具有async/ 已await 启用以运行此代码段(或nodejs v7及更高版本)的浏览器

这样,您可以简单地使用try/ catch来处理错误,并在parallel函数内返回部分结果.

  • 这是一个坏习惯。切勿对两个或多个异步并行任务使用多次等待,因为您将无法认真处理错误。它仅适用于肯定的情况,但在否定的情况下(异步任务拒绝),尽管您使用try / catch,但始终会以未处理的错误结尾。您必须始终将Promise.all用于异步并行任务。在这里查看我的答案:/sf/answers/3800416231/如果您需要全局处理错误,请分别使用此命令:`try {let [val1,val2] =等待Promise.all([task1()。 catch(e => ...),task2()。catch(e => ...)]); } catch(e){}` (3认同)
  • 那段很好的代码!很有帮助 (2认同)
  • 请注意,要在Node.js中运行此示例,您将需要(当前处于实验状态)[Performance Timing API](https://nodejs.org/api/perf_hooks.html#perf_hooks_performance_timing_api)。例如,将其添加到示例的顶部const {performance} = require('perf_hooks');` (2认同)
  • “系列”和“并行”都具有“快速失败”的行为。答案文字具有误导性。 (2认同)

ric*_*cka 18

如果你对Promise.all的失败快速行为和解构赋值语法没问题:

const [userResponse, postsResponse] = await Promise.all([
  fetchUserAsync(),
  fetchPostsAsync(),
]);
Run Code Online (Sandbox Code Playgroud)

  • @Mayhem您需要将该代码包装在异步函数中 (2认同)

Wil*_*lco 5

对于那些询问如何将其扩展到运行时确定的调用次数的人,您可以使用 2 个循环。第一个启动所有任务,第二个等待一切完成

console.clear();

function wait(ms, data) {
  return new Promise( resolve => setTimeout(resolve.bind(this, data), ms) );
}

/** 
 * While here we call the functions first,
 * then wait for the result later, so 
 * this will finish in 500ms.
 */
async function runTasks(timings) {
  let tasks = [];
  for (let i in timings) {
      tasks.push(wait(timings[i], `Result of task ${i}`));
  }

  /* Want fast fail? use Promise.All */
  //return Promise.All(tasks);
  
  let results = [];
  for (let task of tasks) {
       results.push(await task);
  }

  return results;
}

async function taskRunner(fn, arg, label) {
  const startTime = performance.now();
  console.log(`Task ${label} starting...`);
  let result = await fn(arg);
  console.log(`Task ${label} finished in ${ Number.parseInt(performance.now() - startTime) } miliseconds with,`, result);
}

void taskRunner(runTasks, [50,100,200,60,500], 'Task List');
Run Code Online (Sandbox Code Playgroud)