Await in turn by mistake
I really enjoy working with the new async functions but it is really easy to set up a situation where code that could be running in parallel is force to run in sequence. Consider this simple invocation of a javascript function that takes two values returned from other async functions:
const combination = await combine(await value(1), await value(2));
The problem here is that unless the JS environment does some optimisation for you, the actions contained in the two called to await are performed in series.
function action() {
return new Promise(resolve => {
setTimeout(resolve, 5000)
})
}
async function value(v) {
console.log('started ' + v);
return action('broken ' + v).then(()=> {
console.log("finished " + v);
return "ok " + v
})
}
async function test() {
console.log(await value('1') + " " + await value('2'));
}
test();This will result in an output that look something like this, and will take about ten seconds:
(index):39 started 1 (index):39 finished 1 (index):37 started 2 (index):39 finished 2 (index):44 ok 1 ok 2
So there are a number of way to re-write this code in order to ensure that the original processes are all started at the same time, I think the second is the best form, just wish they had put some syntactic sugar so we could do away with Promise.all references.
async function test() {
// Worst
const three = value('3');
const four = value('4');
console.log(await three + " " + await four);
// Better
const [five, six] = await Promise.all([value('5'), value('6')];
console.log(five + " " + six));
// Better?
console.log(...await Promise.all([value('7'), value('8')]))
}
test();So you would expect a test output to be similar to this, with each block taking about the minimum 5 seconds.
(index):37 started 3 (index):37 started 4 (index):39 finished 3 (index):39 finished 4 (index):47 ok 3 ok 4 (index):37 started 5 (index):37 started 6 (index):39 finished 5 (index):39 finished 6 (index):48 ok 5 ok 6 (index):37 started 7 (index):37 started 8 (index):39 finished 7 (index):39 finished 8 (index):48 ok 7 ok 8
It is really easy to make the same mistake when writing a for loop for example by waiting on each item in turn
async function test() {
const list = ['1', '2', '3'];
const result = [];
for (const item of list) {
result.push(await value(item));
}
console.log(result);
}For most operation that involve Promise and loop you will normally need map/Promise.all at some point. This version should complete in around the minimum 5 seconds.
async function test() {
const list = ['1', '2', '3'];
const result = await Promise.all(list.map(value));
console.log(result);
}| Published on Web Code Geeks with permission by Gerard Davison, partner at our WCG program. See the original article here: await in turn by mistake Opinions expressed by Web Code Geeks contributors are their own. |

