简体   繁体   中英

for loop doesn't wait for async code to finish

I have a for loop in which there is a function call and that function further has some async code. Now the issue is that the for loop doesn't wait for the async code to return from the nested function call and continues iterating.

The function aFunctionThatRunsAsync comes from a library that I have no control over.

Below is all the code.

// arr.length = 2

for (let i in arr) {
  console.log(i, 'i1')

  functionOne(arr[i], i, (err, res) => {
    console.log(res)
  })
}

function functionOne(inp, idx, callback) {
  console.log(idx, 'i1')
  const processor = aFunctionThatRunsAsync(inp, err => {
    if (err) return callback(err);
  });

  processor.on('complete', data => {
    console.log(data, idx, 'i3')
    return callback(null, data)
  });
}

Problem:

The log looks like this after the execution of the code:

0 i1 // expected
0 i2 // expected
1 i1 // unexpected, expected to have logged: data(obj) i3
1 i2 // unexpected, expected to have logged: 1 i1

And finally logs:

data(obj) 0 i3
data(obj) 1 i3 // Not always in the same order

I want the for loop to wait for the async code to return/log and run synchronously in the correct order so the final output look like this:

0 i1
0 i2
data(obj) 0 i3
1 i1
1 i2
data(obj) 1 i3

That code doesn't have anything in the for-in to make it wait for those callbacks.

The code in functionOne doesn't look promise-based, so unless you want to change that code, you need to wait for the callback before starting the next iteration, something like this:

process(0);
function process(i) {
  if (i < arr.length) {
    console.log(i, 'i1')
    functionOne(arr[i], i, (err, res) => {
      if (err) {
        console.error(err);
        // I assume you don't want to continue here
      } else {
        console.log(res);
        process(i + 1);
      }
    });
  }
}

Alternatively, you could make functionOne return a promise:

function functionOne(inp, idx) {
  return new Promise((resolve, reject) {
    console.log(idx, 'i1')
    const processor = aFunctionThatReturnsAPromise(inp, err => {
      if (err) return reject(err);
    });

    processor.on('complete', data => {
      console.log(data, idx, 'i3');
      resolve(data);
    });
  })
}

Then:

let promise = Promise.resolve();
for (let i = 0; i < arr.length; ++i) {
    promise = promise.then(() => (
        functionOne(arr[i], i).then(res => {
            console.log(res);
        })
    ));
}
promise.catch(error => console.error(error));

Note that the let inside the for there is important. It can't be var , and it has to be inside the for . That's because we need the callback we pass to then to close over the i for that loop iteration, which it will with let as above but wouldn't with var .

It would be easier to use in an async function:

// In an `async` function
try {
    for (let i = 0; i < arr.length; ++i) {
        console.log(await functionOne(arr[i], i));
    }
} catch (error) {
    console.error(error);
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM