How can I process asynchronous results in order as soon as they resolve in JavaScript?

时间:2017-04-10 02:11:17

标签: javascript node.js asynchronous ecmascript-6 promise

I have an asynchronous function that takes quite a bit of time to call back, that needs to run for an array of inputs, and pipe the results to subsequent operations. My initial approach was Promise-based:

Promise.all(entries.map((entry) => {
  return new Promise((resolve, reject) => {
    processEntry(entry, resolve);
  });
})).then((outputs) => {
  outputs.forEach((output) => {
    return console.log(output);
  });
  callback();
});

The issue with this, is that the outputs only get processed (or in this case logged) after all of them have resolved. In order to have them log as soon as they've resolved, I tried this more old-fashioned approach:

var i = 0;
entries.forEach((entry) => {
  processEntry(entry, (output) => {
    console.log(output);
    if (++i >= entries.length)
      callback();
  });
});

This works for my very particular case, but doesn't seem like a correct general solution, since there's no longer any guarantee that the outputs will be in order.

The best way I can think of so far to achieve the desired effect, is to populate an array with the outputs at their original index in entries, and every time a new output comes, you process the outputs from the start of the array with a separate sliding index that stops when it reaches an undefined.

Is there a more elegant way to achieve this?

2 个答案:

答案 0 :(得分:2)

After reading the comments, you want to kick off all the processEntry but process them in order

entries.map(entry => new Promise((resolve, reject) => {
    processEntry(entry, resolve);
}))
.reduce((promise, entry) => {
    return promise.then(() => entry)
    .then(value => console.log(value));
}, Promise.resolve())
.then(() => {
    // all done at this point
});

If you want a final then with ALL the results

entries.map(entry => new Promise((resolve, reject) => {
    processEntry(entry, resolve);
}))
.reduce((promise, entry) => {
    return promise.then(results => entry.then(value => {
        // process results in order but as soon as possible
        console.log(value)
        // concat to the results
        return results.concat(value);
    }));
}, Promise.resolve([]))
.then(results => {
    // all results in correct order
});

答案 1 :(得分:0)

I see you are use es6 now, maybe you can try to use async/await, you can see the doc

so your code maybe look like as follow:

const processEntry = async (entry) => {
    // ...other code
}

const getFinalResult = async (entries) => {
    const result = [];
    for (let i = 0; i < entries.length; i++) {
        const data = await processEntry(entries[i]);
        result.push(data);
    }
    return result
}

const result = await getFinalResult(entries);

if you aren't familiar with async/await, you can still use promise

then you can try it in this way

let promise = Promise.resolve();
let result  = [];
for (let i = 0; i < entries.length; i++) {
    ((entry) => {
        promise = promise.then(() => {
            const data = processEntry(entry);
            result.push(result);
        })
    }
    )(entries[i)
}

promise.then(() => {
    callback(result)

})