# Building our own Async-Await using Generators and Promises
# Goal: Async-Await ≈ Generators + Promises
Imagine we are given a piece of code like the one below that uses async functions,
function doTask1(arg) {
    return new Promise((resolve, reject) => {
        setTimeout(() => resolve(arg), 100)
    })
}
function doTask2(arg) {
    return new Promise((resolve, reject) => {
        setTimeout(() => resolve(arg+2), 100)
    })
}
function doTask3(arg) {
    return new Promise((resolve, reject) => {
        setTimeout(() => resolve(arg+3), 100)
    })
}
async function init(arg) {
    const res1 = await doTask1(arg);
    console.log(res1);
    
    const res2 = await doTask2(res1);
    console.log(res2);
    const res3 = await doTask3(res2);
    console.log(res3);
    return res3;
}
init(3);
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
It performs three asynchronous tasks, one after the other where each task depends on the completion of the previous task. Finally, it returns the result of the last task.
Goal
- How can we rewrite this code using only promises and generator functions?
- and we don't want ot make a chain of promise.then!
- How can we reinvent/rebuild our own async and await?
# Remember Generators
Remember:
- When a generator function is called, its body is not executed right away.
- Instead it returns an iterator object which adheres to the iterator protocol i.e. it has a nextmethod.
- The only way to execute the body of the generator is by calling the nextmethod on its iterator-object.
- Every time the nextmethod is called, its body is executed until the nextyieldexpression.
- The result of next()is always an object with two properties:- value: the yielded value.
- done:- trueif the function code has finished, otherwise- false
 
- This nextmethod also accepts anargument.
- Calling it with an argument- Makes the argumentthe value of the current yield expression and
- Resumes the execution till the next yieldexpression
 
- Makes the 
# First Idea: Generators can yield Promises
By now you would be wondering, how do the generator functions help to achieve our goal?
We need to model an asynchronous flow where we have to wait for certain tasks to finish before proceeding ahead. How can we do that?
Well, the most important insight here is that the generator-functions can yield promises too.
- A generator function can yield a promise (for example an async task), and
- its iterator can be controlled to halt for this promise to resolve (or reject), and then
- recursively proceed with the resolved (or rejected) value to ask for the next iteration.
# Rewriting the Async Function as a Generator
This pattern of weaving a an iterator with yielded promises allows us to model our requirement like this:
function doTask1(arg) {
    return new Promise((resolve, reject) => {
        setTimeout(() => resolve(arg), 100)
    })
}
function doTask2(arg) {
    return new Promise((resolve, reject) => {
        setTimeout(() => resolve(arg+2), 100)
    })
}
function doTask3(arg) {
    return new Promise((resolve, reject) => {
        setTimeout(() => resolve(arg+3), 100)
    })
}
function* init(arg) {
    const res1 = yield doTask1(arg);
    console.log(res1);
    
    const res2 = yield doTask2(res1);
    console.log(res2);
    const res3 = yield doTask3(res2);
    console.log(res3);
    return res3;
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
Notice how this generator function resembles our async function!
If you change yield for await is the same code!
# Goal
Your goal is to write a function waiter(genFun, arg) that returns a function that when called
waiter(genFun, arg)() will iterate on genFun with initial arguments arg but in such a way that
it will wait for the fulfillment of the promise yielded on each iteration and that will return the final value. It will be used like this:
function* main() {
    const res = yield waiter(init, 3)();
    console.log(`res=${res}`);
}
waiter(main)();
2
3
4
5
6
# First approach: Using .then chains
 To run init and make it wait on echa promise we can  call it this way:
const gen = init(3);
// no async no await
gen.next().value.then(res1 => 
  gen.next(res1).value.then(res2 => 
    gen.next(res2).value.then(res3 => 
      Promise.resolve(gen.next(res3).value).then(returnValue => 
        console.log(`returnValue ${returnValue}`)
        )
    )
  )
)
2
3
4
5
6
7
8
9
10
11
12
The first call to gen.next() yields an object { value: Promise { <pending> }, done: false }. Therefore
the expression gen.next().value.then(res1 => ...)  assures that the handler res1 => ...
will go to the microqueue and be called after the promise returned by doTask1(arg) resolves to 3.
function* init(arg) {
    const res1 = /* Execution pauses here */ yield doTask1(arg); 
    // res1 is 3 after the assignment
    console.log(res1); 
    ...
}
2
3
4
5
6
Similarly, the second call to gen.next(res1) yields an object
{value: Promise, done: false}. Therefore the sub-expression
gen.next(res1).value.then(res2 => ...)  assures that the handler res2 => ...
will be called when the promise returned by doTask2(res1) resolves.
The third call to gen.next(res2) yields an object
{value: Promise, done: false}. Therefore the sub-expression
gen.next(res2).value.then(res3 => ...)  assures that the handler res3 => ...
will be called when the promise returned by doTask3(res2) resolves.
Finally, the expression gen.next(res3) yields an object
{value: Promise, done: false}. Therefore the sub-expression
gen.next(res3).value.then(returnValue => console.log(`returnValue ${returnValue}`))
ouputs the final value.
The execution of the code produces an output like:
node no-async-await-1.js 
3
5
8
returnValue 8
2
3
4
5
Unfortunately, this current solution is not free of .thenchains.
We need something better.
Idea
The problem resembles the series function we wrote in a previous lab where we found a solution
making use of recursivity and detecting when all the tasks were done.
# Write the Function Controlling the Execution of the Generator
Now we need a controlled way to execute init.
We need to write a function waiter that can control the iterator of this generator function to "wait for the fulfillment of the promise yielded on each iteration". It has to:
- Halt every time a promise is yielded and
- Proceeds once the promise resolves (or rejects).
Precise Goal
Write a function waiter(generator, arg) that
/**
 * Builds the generator object for genFun and returns the waiting function
 * @param { generator } genFun - the generator function
 * @param { any } arg     - the argument to pass to the generator function
 * @returns { function }  - a function that executes the generator waiting for each yielded promise
 */
function waiter(genFun, arg) 
2
3
4
5
6
7
- creates a generatorby callinggenFun(arg)and
- returns an auxiliary  function  waitAndrun(Something like we did in the auxiliary callback in theseriesfunction of the asyncmap lab)
- The returned auxiliary function waitAndRunhas to- Get the current promise via .nextand
- Wait for that promise to be fulfilled and
- call recursively itself unless the iterator is exhausted
 
- Get the current promise via 
It will be used like this:
function* main() {
    const res = yield waiter(init, 3)();
    console.log(`res=${res}`);
}
waiter(main)();
2
3
4
5
6
So that, when we run it with the generator above, we obtain:
➜  building-async-await-solution git:(main) node solution.js 
3
5
8
res=8
2
3
4
5
It sounds complicated, but takes only a few lines to implement.