You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何从含异步操作的循环返回数组?JavaScript异步问题求助

Hey there! Let's figure out why your code isn't behaving as expected and fix it, plus break down the JavaScript async concepts that are tripping you up here.

First, let's recap your code and what's happening:

Your Original Code

function foo(items) { 
  var result = [] 
  // Assume under forEach statement take 2 sec per loop 
  items.forEach(function(item) { 
    setTimeout(function() { 
      result.push(item + 2) 
    }, 2000) 
  }) 
  return result 
} 
console.log(foo([1,2,3,4])) 
console.log("Done") 

Current Output

[]
Done

Expected Output

[ 3, 4, 5, 6 ]
Done

Why This Is Happening: JavaScript's Async & Event Loop

JavaScript is a single-threaded language, which means it can only do one thing at a time. But it handles async operations (like setTimeout) using something called the Event Loop. Here's the play-by-play of your code:

  1. When you call foo([1,2,3,4]), the function starts running synchronously:
    • It creates an empty result array.
    • It runs forEach over the items. For each item, it schedules a setTimeout callback to run in 2 seconds—but this callback doesn't run right away. Instead, it gets added to the macrotask queue (a list of tasks waiting for the main thread to be free).
  2. Right after forEach finishes, foo returns the empty result array immediately—none of the setTimeout callbacks have run yet!
  3. The main thread then runs console.log(foo(...)), which prints the empty array, followed by console.log("Done").
  4. Only after all these synchronous tasks are done does the Event Loop pick up the setTimeout callbacks from the queue, one by one, and run them—pushing values into result... but by then, you've already printed the array and "Done".

How to Fix It

We need to wait for all the async setTimeout operations to finish before we access the result array. The cleanest way to do this in modern JavaScript is using Promises with async/await.

Solution 1: Async/Await + Promise.all

async function foo(items) {
  // Wrap each setTimeout in a Promise
  const asyncTasks = items.map(item => {
    return new Promise((resolve) => {
      setTimeout(() => {
        resolve(item + 2); // Resolve with the computed value
      }, 2000);
    });
  });

  // Wait for all Promises to resolve, then get the results array
  const result = await Promise.all(asyncTasks);
  return result;
}

// Since foo returns a Promise, we use .then() to handle the result
foo([1,2,3,4])
  .then(result => {
    console.log(result); // Logs [3,4,5,6]
    console.log("Done"); // Logs "Done" right after
  });

Let's break this down:

  • items.map(...) creates an array of Promises, one for each item. Each Promise resolves to item + 2 after 2 seconds.
  • Promise.all(asyncTasks) waits for every Promise in the array to finish, then returns a single Promise that resolves to an array of all the results (in the same order as the original items).
  • await pauses the execution of the async function until the Promise resolves, so result will be filled with the correct values when we return it.
  • We use .then() to access the resolved result because async functions always return a Promise.

Alternative: Using Callbacks (Older Approach)

If you need to support older environments without async/await, you can use a callback to signal when all tasks are done:

function foo(items, callback) {
  var result = [];
  var completedTasks = 0;

  items.forEach(function(item) {
    setTimeout(function() {
      result.push(item + 2);
      completedTasks++;
      // Check if all tasks are done
      if (completedTasks === items.length) {
        callback(result);
      }
    }, 2000);
  });
}

// Call foo with a callback to handle the result
foo([1,2,3,4], function(result) {
  console.log(result); // Logs [3,4,5,6]
  console.log("Done"); // Logs "Done"
});

Key Takeaways

  • Synchronous code runs immediately: Any code without async operations (like variable declarations, forEach, return) runs right away in the main thread.
  • Async tasks are queued: Operations like setTimeout, AJAX calls, or Promise callbacks don't block the main thread—they're added to a queue and run only after all synchronous code is finished.
  • Use Promises/async/await to handle async results: If you need to wait for async tasks to finish before using their output, you can't just return a variable immediately—you need to use async patterns to wait for the results.

内容的提问来源于stack exchange,提问作者6LYTH3

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.28 04:05:02