简体   繁体   English

Javascript 循环、异步函数和无头浏览器

[英]Javascript loops, async functions and headless browser

By playing with the new headless browser playwright from microsoft, I contsructed something that neither returns an error nore something else.通过与微软的新无头浏览器剧作家一起玩,我构建了既不返回错误也不返回其他内容的东西。

At this time, my ideas are over and I ask you for some hints to point me to my failure.此时,我的想法已经结束,我请求您提供一些提示以指出我的失败。

This piece of code should just start a multiple headless browser group asynchron.这段代码应该只是启动一个多无头浏览器组异步。 But the launch of the browser hangs and the application stays in an endless loop.但是浏览器的启动挂了,应用程序停留在无限循环中。 I paste the code here, it is a simple nodejs script, to reproduce the behaviour.我在这里粘贴代码,它是一个简单的 nodejs 脚本,用于重现行为。

Thanks for help & reading ;)感谢您的帮助和阅读;)

const playwright = require('playwright');

log('start playwright async');

let maxRunners = 1;
let running = 0;
let list = [1,2,3,4,5,6,7,8,9,0,11,12,13,14,15];

log('start job');

while (list.length > 0) {

    if (running < maxRunners) {
        log('runner started');
        running++;

        let entry = list[0];
        list.shift();

        log('start browser loop');
        for (const browserType of ['chromium', 'firefox', 'webkit']) {
            log('fire async');
            (async () => {
                log('loop next');
                log('launch: ', browserType);
                const browser = await playwright[browserType].launch({
                    headless: false
                });
                log(browserType, ' launched');
                const context = await browser.newContext();
                log('open new page');
                const page = await context.newPage('http://whatsmyuseragent.org/');
                log('page opened');
                log('make screenshot');
                await page.screenshot({path: `example-${browserType}.png`});
                log('screenshot made');
                log('close browser');
                await browser.close();
                log('browser closed');
                log('loop succeed');

                running--;
            })();
            log('end async');
        }
        log('end loop');

        if (running === 0 && list.length === 0) {
            log('job finished');
        }
    }
}

log('end playwright script');

function log(...msgs) {
    let date = new Date();
    let timeString = date.toISOString().substr(11, 8);
    let msg = '';
    for (let i in msgs) {
        msg += msgs[i];
    }

    console.log(timeString, ':', msg);
}

The output:输出:

20:53:29 : start playwright async
20:53:29 : start job
20:53:29 : runner started
20:53:29 : start browser loop
20:53:29 : fire async
20:53:29 : loop next
20:53:29 : launch: chromium
20:53:29 : end async
20:53:29 : fire async
20:53:29 : loop next
20:53:29 : launch: firefox
20:53:29 : end async
20:53:29 : fire async
20:53:29 : loop next
20:53:29 : launch: webkit
20:53:29 : end async
20:53:29 : end loop

There are a few things you can improve in your code:您可以在代码中改进以下几点:

(async()=>{

  log('start playwright async');

  let maxRunners = 1;
  let running = 0;
  let list = [1,2,3,4,5,6,7,8,9,0,11,12,13,14,15];

  log('start job');
  const promises = [];
  while (list.length > 0) {

      if (running < maxRunners) {
          log('runner started');
          running++;

          let entry = list[0];
          list.shift();

          log('start browser loop');
          for (const browserType of ['chromium', 'firefox', 'webkit']) {
              log('fire async');
              promises.push((async () => {
                  log('loop next');
                  log('launch: ', browserType);
                  const browser = await playwright[browserType].launch({
                      headless: false
                  });
                  log(browserType, ' launched');
                  const context = await browser.newContext();
                  log('open new page');
                  const page = await context.newPage('http://whatsmyuseragent.org/');
                  log('page opened');
                  log('make screenshot');
                  await page.screenshot({path: `example-${browserType}.png`});
                  log('screenshot made');
                  log('close browser');
                  await browser.close();
                  log('browser closed');
                  log('loop succeed');

                  running--;
              })());
              log('end async');
          }
          log('end loop');
      } else {
        await Promise.all(promises);
      }
  }

  await Promise.all(promises);
  log('job finished');
  log('end playwright script');

  function log(...msgs) {
      let date = new Date();
      let timeString = date.toISOString().substr(11, 8);
      //date.setSeconds(45); // specify value for SECONDS here
      //var timeString = date.toISOString().substr(11, 8);
      let msg = '';
      for (let i in msgs) {
          msg += msgs[i];
      }

      console.log(timeString, ':', msg);
  }
})()

Let's wrap everything in an async function让我们将所有内容包装在一个异步函数中

(async()=>{
)();

Then, let's keep track of those tasks/promises:然后,让我们跟踪这些任务/承诺:

const promises = [];
...
log('fire async');
promises.push((async () => {
})());

If you get out of workers you need to wait for them:如果你离开工人,你需要等待他们:

if (running < maxRunners) {
...
} else {
   await Promise.all(promises);
}

You should get running with this.你应该开始使用这个。

I do not believe that your async function is actually being evaluated.我不相信您的异步功能实际上正在评估。

Instead of calling your async function once per iteration, could you create a list of promises and use Promise.all() ?您可以创建一个承诺列表并使用Promise.all() ,而不是每次迭代调用一次异步函数吗?

Immediately-Invoked functions are executed without waiting to finish.立即调用的函数无需等待完成即可执行。 For example:例如:

 const promise = (time = 1, shouldThrowError = false) => new Promise((resolve, reject) => { timeInMs = time * 1000 setTimeout(()=>{ console.log(`Waited ${time} secs`) if (shouldThrowError) reject(new Error('Promise failed')) resolve(time) }, timeInMs) }); // Start excuting first async immediate function (async () => { try { console.log('starting first promise') await promise(1) console.log('finished first promise') } catch (error) { } })(); // This executes without finishing previous promise (async () => { try { console.log('starting second promise') await promise(1) console.log('finished second promise') } catch (error) { } })();

Change your block code:更改您的块代码:

        (async () => {
            log('loop next');
            ...
            log('loop succeed');

            running--;
        })();

to:到:

        const process = async () => {
            log('loop next');
            ...
            log('loop succeed');

            running--;
        };
        await process()

Also, in order to be able to use await, you should wrap all your code in an async function:此外,为了能够使用 await,您应该将所有代码包装在一个异步函数中:

(async () => {
   ...all your code
})();

On the same line as per the excellent answer of @hardkoded:与@hardkoded 的优秀回答在同一行:

In short use async functions with promises:简而言之,使用带有承诺的异步函数:

An basic example to illustrate the same point simply :一个简单地说明同一点的基本示例:

function delay() {
  return new Promise(resolve => setTimeout(resolve, 300));
}

async function delayedLog(item) {
  // notice that we can await a function
  // that returns a promise
  await delay();
  console.log(item);
}



async function processArray(array) {
  // map array to promises
  const promises = array.map(delayedLog);
  // wait until all promises are resolved
  await Promise.all(promises);
  console.log('Done!');
}

Try it , play with it to understand async behaviour in javaScript!试试看,玩弄它以了解 javaScript 中的异步行为!

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM