首页 > 解决方案 > 模块范围变量拒绝被异步函数覆盖。为什么?

问题描述

模块范围变量“输出”拒绝被异步函数“retrieveTextWrapper”覆盖,我不知道为什么。我的目标是在 StackOverFlow 的主页上输出文本。retrieveTextWrapper 成功地抓取了这些信息,但我似乎无法将此内容分配给输出变量。我究竟做错了什么?如何从 main() 函数打印抓取的信息?

注意:我使用的是电子版本 3.0.4,因为绕过 CORS 在该版本上不那么痛苦。

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)

标签: javascriptnode.jsasynchronouselectron

解决方案


win.webContents.once()不返回承诺(因为接口通常不会同时接受回调和返回承诺)。

因此await不等待异步操作完成。因此,您在重新分配其值之前查看输出。仅当您连接到您尝试等待的异步操作的承诺await时才会做一些有用的事情。await

要确认这个时序问题,在 ofconsole.log()之前和之后添加一个唯一的语句,然后您可以看到这些日志消息的顺序。await win.webContents.once('dom-ready',retrieveTextWrapper);retrieveTextWrapper

是的,一切都在retrieveTextWrapper功能范围内发生变化。你的解释很有道理。但是,是否可以等待回调完成(使用 await 之外的其他语法)?这样,我可以将更新后的值用于主函数中的其他操作吗?

你有几个选择。

你可以“承诺” win.webContents.once(),这样你就可以使用await它了。

您可以将回调内联并将其余代码main放入该回调中(处理异步操作的经典方式)。

这是一个承诺的例子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);
    });
}

然后,您可以像这样使用它:

async function main(){
   navigate();
   win.openDevTools();
   await waitForDomReady();
   await retrieveTextWrapper();
   console.log(output);
}

这假设retrieveTextWrapper调用中的代码在win.webContents.executeJavaScript()完成时确实返回了一个承诺。如果没有,你也必须承诺。


推荐阅读