Use Async / await while assigning value to global const variable

Viewed 4405

I'm trying to declare and initialize a const variable with global scope for the module and it needs to get value from the result of an async/await. Here is the code I've tried and the variable still holds undefined. How can I re-write this so I can still use a constant and this holds the desired value:

const puppet = require('puppeteer')
const browser = (async () => { await puppet.launch() })();
const page = (async () => { await browser.newPage() })();

console.log( "browser holds: " + util.inspect(page) );
     // prints out undefined

I need to setup two separate pages that will be used throughout the app to load different pages and process them as needed.

For now, I'm able to do this by declaring the variables as 'var' and then assigning the value in a .then() . But I'd prefer to use a 'const' for these as a best practice.

Here is the code that lets me do this as a var and use it through out the app:

const puppet = require('puppeteer')

async function setupNewPuppetWithKeys(puppetBrowser, puppetPage ){

  let browser = await puppet.launch();
  let page = await browser.newPage();
  page.setViewport({ width: 1280, height: 800 });

  logger.infoLog("initialized values for - " + puppetBrowser + ", " + puppetPage )
  return { [puppetBrowser] : browser , [puppetPage] : page };
}

var browserDev, pageDev;
setupNewPuppetWithKeys("browserDev", "pageDeV").then( (jsonResult) => {

   logger.infoLog("In Then received : " + util.inspect(jsonResult) );
   browserDev = jsonResult.browserDev;
   pageDev = jsonResult.pageDev;
} );

// I declare more browser and page variables here .. but you get the idea with the one I've done above.
3 Answers

For the moment, top-level async / await is not supported. What you can do is create a "main" async function that you can call at the top-level :

const puppet = require('puppeteer')

async function main() {
  const browser = await puppet.launch();
  const page = await browser.newPage();

  console.log( "browser holds: " + util.inspect(page) );
}

main();
const puppet = require('puppeteer')

async function main(){
const browser = await puppet.launch();
const page = await browser.newPage();

console.log( "browser holds: " + util.inspect(page) );
}

You don't need to define independent functions, instead wrap all of them inside an async function (as await can be used only in async functions) now you ll get the desired values.

I had the same problem with node.js and could not find an answer, but then tried this technique, which might be helpful if you are using node.js.

The trick is to define the read-only variable on the global object within an async function. After that function has finished, then other code can directly access the variable with global range across the whole application.

Here is an test script that illustrates the approach. If anyone has a simpler solution I would be interested to know.

    "use strict";

    // Utility function to set a constant value on the global object
    function setGlobalConst(constName, constValue){
        console.log(`      >> setGlobalConst ${constName} `);
        if( typeof global[constName] !== "undefined" )
            throw new Error(`Attempt to redefine global const ${constName}`);
        Object.defineProperty(global, constName, { value: constValue, writable: false });
    };

    // This is the setter function for the global const ABC
    // Note this does not need to be async, it returns a promise
    // synchronously
    function setABC () {
       // Check for the global promise to set the value of ABC
        if(typeof global.prmABC === "undefined" ){ 
            console.log("      >> setABC called for the first time")

            // Create the promise that will set the value of ABC some
            // time in the future;
            let setterPromise =
                (
                    async function(){
                        await delay(2000);
                        setGlobalConst("ABC", "[VALUE OF ABC]");
                        console.log("      >> setter: global const ABC has now been set")
                        return ABC;
                        }
                )()

                // Save this promise in the global setter promise const prmABC
                setGlobalConst("prmABC", setterPromise );

                // Return the promise, which will resolve when ABC has been set
                return setterPromise;

        };

        // As this has previously been called, we just
        // need to return the promise that was previously
        // saved in the global const prmABC.
        console.log("      >> setABC has already been called")
        return prmABC;
        
    };

    // Application code - this is async to ensure that
    // it waits for the ABC setter to resolve before accessing
    // the global const ABC.
    async function run( ){
        console.log(`    1. Before setting, global ABC=${global.ABC}`);

        console.log("    2. Calling setABC without waiting");
        setABC();

        console.log(`    3. Before setter has resolved, global ABC still =${global.ABC}`)

        try{
            console.log("    4. Waiting on the original global setter promise.. delay here...:");
            let myABC1 = await prmABC;
            // The value returned by the resolved prmABC is the value of global const ABC
            console.log(`       4a. >> Resolved value of global prmABC=${myABC1}`); 
            // Also, now that the setter promise is fulfilled, the global const ABC is set
            console.log(`       4b. >> Global const ABC=${ABC}`); 

            console.log("    5. We can also get the value of ABC by waiting on a call to setABC()");
            let myABC2 = await setABC();

            console.log(`       5a. >> Value returned from resolved setABC() is ${myABC2}`);

            console.log(`    7. Accessing value of ABC directly: ABC=${ABC}`);

        } catch(e) {
            console.log(`    ERROR: ${e.message}`)
        };

        console.log("    8. Calling setABC() again, after it has finished, without waiting");
        setABC();
        
        console.log(`    9. Accessing value of global const ABC again: ABC=${ABC}`);

        try{
            console.log("    10. Attempting to change value of ABC directly")
            ABC = "New value of ABC";
        } catch(e) {
            console.log(`    ERROR: ${e.message}`)
        };

        console.log("    Run finished---------------------------------");
    };

    run();

    // Small utility function to provide async promise
    function delay(ms) { 
        return new Promise(resolve => setTimeout(resolve, ms)); 
    } 

The console output from this code is:

test> node testConstWithAsync
1. Before setting, global ABC=undefined
2. Calling setABC without waiting
  >> setABC called for the first time
  >> setGlobalConst prmABC
3. Before setter has resolved, global ABC still =undefined
4. Waiting on the original global setter promise.. delay here...:
  >> setGlobalConst ABC
  >> setter: global const ABC has now been set
   4a. >> Resolved value of global prmABC=[VALUE OF ABC]
   4b. >> Global const ABC=[VALUE OF ABC]
5. We can also get the value of ABC by waiting on a call to setABC()
  >> setABC has already been called
   5a. >> Value returned from resolved setABC() is [VALUE OF ABC]
7. Accessing value of ABC directly: ABC=[VALUE OF ABC]
8. Calling setABC() again, after it has finished, without waiting
  >> setABC has already been called
9. Accessing value of global const ABC again: ABC=[VALUE OF ABC]
10. Attempting to change value of ABC directly
ERROR: Cannot assign to read only property 'ABC' of object '#<Object>'
Run finished---------------------------------
Related