Search code examples
javascriptnode.jsasynchronousecmascript-6

Extract part of code from an async/await block into a separate function


I have an async anonymous self-invoking function. The word “async” before the function means the function will always returns a promise. If the code has return in it, then JavaScript automatically wraps it into a resolved promise with that value.

This async allows us to use "wait". The keyword await makes JavaScript wait until that promise settles and returns its result. So the function execution “pauses” and resumes when the promise settles. That doesn’t cost any CPU resources, because the engine can do other jobs meanwhile: execute other scripts, handle events etc.

(async () => {
    await page.goto('test.com', { waituntil: ['load', 'domcontentloaded', 'networkidle0'] });
    await page.evaluate((user, pass) => {
      document.querySelector('#user').setAttribute('value', user);
    }, user, pass)
    await page.click('button[type=submit].btn-primary');


    for(var i = 0; i < 10; i++){
        try { 
            await page.goto('test.com/second', { waituntil: ['load', 'domcontentloaded', 'networkidle0'] });
        } catch(e) {
                await page.goto('test.com', { waituntil: ['load', 'domcontentloaded', 'networkidle0'] });
                await page.evaluate((user, pass) => {
                    document.querySelector('#user').setAttribute('value', user);
                }, user, pass)
                await page.click('button[type=submit].btn-primary');
        } 

        // more stuff not relevant here
    }    

})();

But as you can see above, I have duplicate code. I want to extract the duplicate code into a separate function. Something like this:

(async () => {

    logIn()

    for(var i = 0; i < 10; i++){
        try { 
            await page.goto('test.com/second', { waituntil: ['load', 'domcontentloaded', 'networkidle0'] });
        } catch(e) {
            logIn()
        } 

        // more stuff not relevant here
    }    

})();


function logIn(){
                await page.goto('test.com', { waituntil: ['load', 'domcontentloaded', 'networkidle0'] });
                await page.evaluate((user, pass) => {
                    document.querySelector('#user').setAttribute('value', user);
                }, user, pass)
                await page.click('button[type=submit].btn-primary');
}

However, this won't work because await only works inside an aysnc. Now one idea is to place await in front of the logIn call like this:

await logIn()            

But here is the thing. Inside that logIn function, I purposely use await 3 times because I need one statement to be resolved before the next.

How can I achieve this? Use async/await and use my logIn function?


Solution

  • Make your logIn function as an async function too:

    async function logIn() {...}
    

    then, when you call it, await for it:

    await logIn();
    

    like so:

    async function logIn() {
      await page.goto('test.com', {
        waituntil: ['load', 'domcontentloaded', 'networkidle0']
      });
      await page.evaluate((user, pass) => {
        document.querySelector('#user').setAttribute('value', user);
      }, user, pass)
      await page.click('button[type=submit].btn-primary');
    }
    
    (async() => {
      await logIn();
    
      for (var i = 0; i < 10; i++) {
        try {
          await page.goto('test.com/second', {
            waituntil: ['load', 'domcontentloaded', 'networkidle0']
          });
        } catch (e) {
          logIn()
        }
    
        // more stuff not relevant here
      }
    })();
    

    More on async functions