0

I have two asynchronous operations such that the invocation of the second one uses input from the output of the first. To implement such invocations with async and await, it seems, it won't turn out to be too different from callbacks.

Consider this.

async function loop(label, I) {
    console.log('%s - Looping for %d times.', label, I)
    console.time(label)
    for (i = 0; i < I; ++i) {
    }
    return Promise.resolve(I)
}
//
async function demo0() {
    // Refer - https://stackoverflow.com/a/45479579/919480
    let I0 = loop('01', 10)
    let I1 = loop('02', I0 * 1000)
    await I0
    await I1
}
//
async function demo1() {
    // Refer - https://stackoverflow.com/a/45479579/919480
    let I0 = loop('11', 10)
    await I0
    let I1 = loop('12', I0 * 1000)
    await I1
}
//
async function demo2() {
    await loop('21', 10).then(async (i) => {
        await loop('22', i * 1000)
    })
}
//
(async () => {
    await demo0()
    await demo1()
    await demo2()
})()

Result:

01 - Looping for 10 times.
02 - Looping for NaN times.
11 - Looping for 10 times.
12 - Looping for NaN times.
21 - Looping for 10 times.
22 - Looping for 10000 times.

The second loop should iterate based on a value passed on by the first loop. In demo0 and demo1, the second loop receives a NaN because they are triggered simultaneously. Only in demo2, does the second loop receive the correct value. One could have achieved this behavior with callbacks too.

Is there a async/await way to achieve this behavior?

cogitoergosum
  • 2,309
  • 4
  • 38
  • 62

1 Answers1

3

Every call of an async function gives you a Promise in return, but Promises can't be (sensibly) added to numbers, so you get NaN in return.

If you want to be able to use the result of a Promise, you should await it and use the resulting expression. If you await the Promise and then try to use the original Promise, you'll still have a Promise, not a value, so you should assign the awaited Promise to a variable, eg change

let I0 = loop('01', 10)
let I1 = loop('02', I0 * 1000)
await I0
await I1

to

let I0 = loop('01', 10)
const resolveValueI0 = await I0;
let I1 = loop('02', resolveValueI0 * 1000)
await I1

(you cannot call the second loop until I0 is done, because the second loop needs the number from the resolution of I0's Promise. Either that, or pass the Promise to I1, and have I1 properly consume it with .then or await)

and

let I0 = loop('11', 10)
await I0
let I1 = loop('12', I0 * 1000)
await I1

to

let I0 = loop('11', 10)
const resolveValueI0 = await I0;
let I1 = loop('12', resolveValueI0 * 1000)
await I1

async function loop(label, I) {
    console.log('%s - Looping for %d times.', label, I)
    console.time(label)
    for (i = 0; i < I; ++i) {
    }
    return Promise.resolve(I)
}
//
async function demo0() {
    // Refer - https://stackoverflow.com/a/45479579/919480
    let I0 = await loop('01', 10)
    let I1 = loop('02', I0 * 1000)
    await I0
    await I1
}
//
async function demo1() {
    // Refer - https://stackoverflow.com/a/45479579/919480
    let I0 = loop('11', 10)
    const result = await I0
    let I1 = loop('12', result * 1000)
    await I1
}
//
async function demo2() {
    await loop('21', 10).then(async (i) => {
        await loop('22', i * 1000)
    })
}
//
(async () => {
    await demo0()
    await demo1()
    await demo2()
})()
CertainPerformance
  • 356,069
  • 52
  • 309
  • 320
  • hmm...yes, I missed the one about the `Promise` _resolution_. Nevertheless, this is another option too, right? `let I0 = await loop('01', 10); let I1 = await loop('02', I0 * 1000)`. – cogitoergosum Oct 19 '19 at 08:34
  • 1
    Yes, and that's actually the preferred option, as long as it doesn't confuse you - fewer unnecessary variables is usually a good thing. – CertainPerformance Oct 19 '19 at 21:24