0

Assume this code:


const puppeteer = require('puppeteer');
const express = require('express')

const app = express()
app.use(express.json())

let browser = undefined;
let page = undefined;

let type = async ( selector, typeString) => {
    await page.waitForSelector(selector)
   //...
}

let setup = async function () {
    browser = await puppeteer.launch({headless: false});
    page = await browser.newPage();
   //...
}
app.post ('/login',  (req, res) => {
    console.log("/login", " req.body: ",req.body)
    setup()
    console.log(page) //undefined
    type(usernameInputSelector, req.body.username)
    type( passwordInputSelector,req.body.password)
    res.end() 
})
//...

I send an http request to fire app.post callback. I am trying to figure out how comes that after setup() , page remains undefined even though setup defines it and sets a value, later when I use type() , the program crashes because page is undefined.

I tried to delete setup() function and instead just copy paste its content into the app.post(), and it works! I assume its something with scoping, but not sure what it is.

Max
  • 1
  • 2
  • 3
    You need to await `setup`. – DallogFheir Aug 20 '23 at 13:17
  • why? app.post is synchronus.. – Max Aug 20 '23 at 13:22
  • 2
    But `setup` isn't? – DallogFheir Aug 20 '23 at 13:23
  • I dont understand. Inside setup, all rows are on await. so how could it possibly finish running and return before page is setup? how could type() be fired before setup() is finished? – Max Aug 20 '23 at 13:28
  • 2
    @Max, As soon as `setup` hits an `await`, it immediately returns a promise. If you don't `await` that promise, app.post will immediately move on and log `page`, which is still `undefined`. Some time later, `setup` will resume running. If you want to wait for all the code in `setup` to finish , you must `await` the promise. – Nicholas Tower Aug 20 '23 at 13:29
  • @nicholasTower I think I understand, but in order to to what you said, I need to make app.post asynchronous to use await, but i want app.post to be synchronous. I want to avoid typing "await" on all of the app.post() code lines just to make it sychronous. making setup() synchronous is also not possible, because it is using puppeteer and it's functions are asynchronous and must be waited for. – Max Aug 20 '23 at 13:41
  • If you don't like using `await`, your other option is to use `.then`. But since puppeteer uses promises, setup must use promises, which means app.post must use promises. It's promises all the way down. – Nicholas Tower Aug 20 '23 at 13:47
  • Option 1 with await: https://pastebin.com/V75RJ2Ad, option 2 with .then: https://pastebin.com/n4XRiksr – Nicholas Tower Aug 20 '23 at 13:54
  • sounds strange that If one writes a big project with 10000+ lines of code, all based on puppeteer or some other asynchronous library, it will have to start every line with await throughout the whole project. Is it actually what will have to happen? – Max Aug 20 '23 at 13:58
  • Well, yes. If you're writing a lot of asynchronous code, expect to write `await` a lot. – Nicholas Tower Aug 20 '23 at 15:32
  • @Max Yes, you'll have to await everything in that 10k+ LOC project. There's a classic blog post from 2015: [What Color is Your Function?](https://journal.stuffwithstuff.com/2015/02/01/what-color-is-your-function/). Rule of thumb: basically all Puppeteer calls need to be awaited. If you write a function that has any async code in it (i.e. Puppeteer code), that also needs to be awaited. You can never go back to sync once you go async. See [this](https://stackoverflow.com/a/67910262/6243352) for an example of using Express with Puppeteer. – ggorlen Aug 20 '23 at 16:34

0 Answers0