【问题标题】:Where is my synchronous function? Trying to call "await" but I get the error "await is only valid in async function."我的同步功能在哪里?尝试调用“等待”,但我收到错误“等待仅在异步函数中有效”。
【发布时间】:2021-05-06 09:03:32
【问题描述】:

尝试编写使用 puppeteer 登录网站的脚本,并在设备通过每个 url 时模拟设备列表。但是,当我尝试运行脚本时出现错误:SyntaxError: await is only valid in async function。我对异步函数不是很熟悉,所以我试图让每个函数都异步,我找不到我的同步函数。如果是当我映射每个 url 时,我如何使这个同步?

//import devices from './puppeteerDevices'
const puppeteer = require('puppeteer');
const { devices } = puppeteer;



testedMobileDevices = ["Galaxy Note 3","Galaxy Note 3 landscape","Galaxy Note II","Galaxy Note II landscape","Galaxy S III","Galaxy S III landscape","Galaxy S5","Galaxy S5 landscape","iPad","iPad landscape","iPad Mini","iPad Mini landscape","iPad Pro","iPad Pro landscape","iPhone 4","iPhone 4 landscape","iPhone 5","iPhone 5 landscape","iPhone 6","iPhone 6 landscape","iPhone 6 Plus","iPhone 6 Plus landscape","iPhone 7","iPhone 7 landscape","iPhone 7 Plus","iPhone 7 Plus landscape","iPhone 8","iPhone 8 landscape","iPhone 8 Plus","iPhone 8 Plus landscape","iPhone SE","iPhone SE landscape","iPhone X","iPhone X landscape","iPhone XR","iPhone XR landscape","iPhone 11","iPhone 11 landscape","iPhone 11 Pro","iPhone 11 Pro landscape","iPhone 11 Pro Max","iPhone 11 Pro Max landscape","Nexus 10","Nexus 10 landscape","Nexus 4","Nexus 4 landscape","Nexus 5","Nexus 5 landscape","Nexus 5X","Nexus 5X landscape","Nexus 6","Nexus 6 landscape","Nexus 6P","Nexus 6P landscape","Nexus 7","Nexus 7 landscape","Pixel 2","Pixel 2 landscape","Pixel 2 XL","Pixel 2 XL landscape"];

const login = async () => {
    const browser = await puppeteer.launch( {headless: true });
    const page = await browser.newPage();
    const username = 'andrewbregman'
    const password = 'randompassword120948acndkla'  
    const base_url = 'http://127.0.0.1:8000/'

  await page.goto({base_url}/login);
  await page.type ('[name=username]', username);
  await page.type('[name=password]', password);
  await page.click('[type=submit]');
  await page.waitFor(5000);
  
}

const test = async () => {
    testedMobileDevices.map(device => {
    login();
    const base_url = 'http://127.0.0.1:8000/'
    const urls = ['myprojects', 'home', 'notifications']
    const browser = await puppeteer.launch()
    const page = await browser.newPage()

    await page.emulate(device)

    urls.forEach(goUrls); 
    const goUrls = async (url) =>{
        await page.goto({base_url}/url)
        page.screenshot({
            path: "./screenshot.jpg",
            type: "jpeg",
            fullPage: true})
        await page.waitFor(100);
    }
    })
}

test();

【问题讨论】:

  • 错误信息应该有一个行号。
  • .map(device => { 至少是一个,但通常你不能只是在前面拍async 就可以了。
  • page.goto({base_url}/url) 无效。你的意思可能是page.goto(`${base_url}/${url}`)

标签: javascript node.js dictionary asynchronous puppeteer


【解决方案1】:

看来您应该一次对这些进行排序,否则您可能会有大量无头 chrome 浏览器并行竞争本地资源。因此,总而言之,您需要进行以下更改:

  1. 依次对操作进行排序以避免并行资源过多。
  2. 正确地与调用者沟通完成或错误。
  3. 正确声明了您的字符串模板。
  4. 在所有返回承诺的操作中使用await
  5. 删除所有使用.forEach(),因为它不是promise-aware 和.map(),因为你真的不想同时运行所有100 个左右。将其替换为常规的 for 循环,该循环继承父级的 async 声明,并且可以感知承诺并且易于排序。
  6. 当您调用它时,记录完成并捕获并记录任何错误。
  7. 按照编码,这将在第一个错误时中止。您可以修改代码以捕获任何错误、记录它、清除该错误并在下一次迭代中继续循环。在继续之前,您需要在几个地方使用 try/catchcatch 块中的正确代码进行清理。

仅供参考,这是您环境中的代码,因此这代表了您应该针对的结构 - 您可能需要修复其他编码错误(因为我已经注意到模板字符串声明的编码错误 - 可能还有其他错误)。

以下是完成所有这些的方法:

//import devices from './puppeteerDevices'
const puppeteer = require('puppeteer');
const { devices } = puppeteer;

const testedMobileDevices = ["Galaxy Note 3", "Galaxy Note 3 landscape", "Galaxy Note II", "Galaxy Note II landscape",
    "Galaxy S III", "Galaxy S III landscape", "Galaxy S5", "Galaxy S5 landscape", "iPad", "iPad landscape",
    "iPad Mini", "iPad Mini landscape", "iPad Pro", "iPad Pro landscape", "iPhone 4", "iPhone 4 landscape",
    "iPhone 5", "iPhone 5 landscape", "iPhone 6", "iPhone 6 landscape", "iPhone 6 Plus", "iPhone 6 Plus landscape",
    "iPhone 7", "iPhone 7 landscape", "iPhone 7 Plus", "iPhone 7 Plus landscape", "iPhone 8", "iPhone 8 landscape",
    "iPhone 8 Plus", "iPhone 8 Plus landscape", "iPhone SE", "iPhone SE landscape", "iPhone X",
    "iPhone X landscape", "iPhone XR", "iPhone XR landscape", "iPhone 11", "iPhone 11 landscape", "iPhone 11 Pro",
    "iPhone 11 Pro landscape", "iPhone 11 Pro Max", "iPhone 11 Pro Max landscape", "Nexus 10", "Nexus 10 landscape",
    "Nexus 4", "Nexus 4 landscape", "Nexus 5", "Nexus 5 landscape", "Nexus 5X", "Nexus 5X landscape", "Nexus 6",
    "Nexus 6 landscape", "Nexus 6P", "Nexus 6P landscape", "Nexus 7", "Nexus 7 landscape", "Pixel 2",
    "Pixel 2 landscape", "Pixel 2 XL", "Pixel 2 XL landscape"
];

async function login() {
    const browser = await puppeteer.launch({ headless: true });
    const page = await browser.newPage();
    const username = 'andrewbregman';
    const password = 'randompassword120948acndkla';
    const base_url = 'http://127.0.0.1:8000/';

    await page.goto(`${base_url}/login`);
    await page.type('[name=username]', username);
    await page.type('[name=password]', password);
    await page.click('[type=submit]');
    await page.waitFor(5000);
}

async function test() {
    for (let device of testedMobileDevices) {
        await login();
        const base_url = 'http://127.0.0.1:8000/'
        const urls = ['myprojects', 'home', 'notifications']
        const browser = await puppeteer.launch()
        const page = await browser.newPage()

        await page.emulate(device);

        for (let url or urls) {
            await page.goto(`${ base_url }/url`);
            await page.screenshot({
                path: "./screenshot.jpg",
                type: "jpeg",
                fullPage: true
            });
            await page.waitFor(100);
        }
    }
}

test().then(() => {
    console.log("all done");
}).catch(err => {
    console.log(err);
});

【讨论】:

  • 很好的回应,谢谢!您是否推荐任何资源来更深入地学习异步函数?
  • @AndrewBregman - 首先确保您完全了解 Promise,因为 awaitasync 仅在您将异步操作绑定到 Promise 时才有用。一些资源:Using promisesAsync/awaitModern Javascript with promises and async/await
  • 仅供参考,我刚刚发布了另一个与此相关的问题。 testsMobileDevices 列表返回不可迭代的错误。我查找了类似的问题,但找不到兼容的解决方案,如果您想查看问题并回答它,我刚刚发了一个新帖子。谢谢!
  • @AndrewBregman - 看来你已经有了答案。您的数组声明不正确。它应该按照我在这里的回答中的方式工作。
  • 好的,很抱歉出现错误,但我收到错误 TypeError: cannot read property 'isMobile' of undefined?
【解决方案2】:

你需要制作传递给testedMobileDevices.mapasync的函数:

const test = async () => {
testedMobileDevices.map(async(device) => {
login();
const base_url = 'http://127.0.0.1:8000/'
const urls = ['myprojects', 'home', 'notifications']
const browser = await puppeteer.launch()
const page = await browser.newPage()

await page.emulate(device)

urls.forEach(goUrls); 
const goUrls = async (url) =>{
    await page.goto({base_url}/url)
    page.screenshot({
        path: "./screenshot.jpg",
        type: "jpeg",
        fullPage: true})
    await page.waitFor(100);
}
})}

【讨论】:

  • 如果不查看返回值,使用.map() 毫无意义,而.map() 无论如何也不关心async 函数。唯一要做的就是将返回值传递给Promise.all() 或类似的东西。
  • 仅供参考,此实现将无法让调用者知道它何时完成或是否有错误。
  • @jfriend00 好的。所以要解决这个问题?我应该把 try and catch(err) 放在哪里?
  • @dev55555 在使用此实现运行脚本后,我收到未处理的承诺拒绝警告。
  • 解释这应该如何写比简单的评论更复杂,所以我添加了自己的答案来说明。
猜你喜欢
  • 2021-04-28
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-02-03
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-02-16
相关资源
最近更新 更多