The module-scope variable "output" refuses to be overwritten by the async function "retrieveTextWrapper", and I cannot figure out why. My objective is to output the text on StackOverFlow's homepage. retrieveTextWrapper successfully scrapes this information, but I can't seem to assign this content to the output variable. What am I doing wrong? How can I print the scraped information from the main() function?
Note: I am using electron version 3.0.4 because bypassing CORS is less of a pain on that version.
const {BrowserWindow, app} = require('electron')
output = "this should be overwritten by the retrieveTextWrapper method"
async function main(){
navigate();
win.openDevTools();
await win.webContents.once('dom-ready',retrieveTextWrapper);
console.log(output);
//prints "this should be overwritten by the retrieveTextWrapper method"
}
function navigate() {
win = new BrowserWindow({width:900,height:900});
win.loadURL(`https://stackoverflow.com/`);
}
function retrieveText(){
return `document.querySelector("*").innerText`;
}
async function retrieveTextWrapper(){
output = await win.webContents.executeJavaScript(retrieveText().replace("*", "#content"));
}
app.on('ready',main)
win.webContents.once()
does not return a promise (since interfaces generally don't accept both callbacks and return a promise at the same time).
Therefore await
doesn't wait for the asynchronous operation to complete. Therefore, you're looking at output before its value has been reassigned. await
only does something useful when you await
a promise that is connected to the asynchronous operation you're trying to wait for.
To confirm this timing issue, add a unique console.log()
statement before and after the await win.webContents.once('dom-ready',retrieveTextWrapper);
and inside of retrieveTextWrapper
and then you can see the sequencing of these log messages.
Yep, everything changes as it should within
retrieveTextWrapper
function. And your explanation makes a lot of sense. However, is it possible to wait for the callback to finish (using some other syntax aside from await)? That way, I can use the updated value for other operations in the main function?
You have a couple options.
You could "promisify" win.webContents.once()
so you could then use await
with it.
You could put the callback inline and put the rest of your code in main
inside that callback (a classic way of dealing with asynchronous operations).
Here's an example of promisifying win.webContents.once()
:
function waitForDomReady() {
return new Promise((resolve, reject) => {
// may want to check if document already has dom-ready and resolve immediately
win.webContents.once('dom-ready', resolve);
});
}
And, you could then use it like this:
async function main(){
navigate();
win.openDevTools();
await waitForDomReady();
await retrieveTextWrapper();
console.log(output);
}
This assumes that the code in retrieveTextWrapper
that calls win.webContents.executeJavaScript()
does actually return a promise when it's done. If not, you have to promisify that too.
Collected from the Internet
Please contact [email protected] to delete if infringement.
Comments