简体   繁体   中英

Chaining a variable number of promises synchronously that share a resource in Javascript

I'm trying to make use of Chrome lighthouse to check a number of urls from a list. I can do this with the one url successfully using their sample code.

function launchChromeAndRunLighthouse(url, opts, config = null) {
  return chromeLauncher.launch({chromeFlags: opts.chromeFlags}).then(chrome => {
    opts.port = chrome.port;
    return lighthouse(url, opts, config).then(results => {
      // use results.lhr for the JS-consumeable output
      // https://github.com/GoogleChrome/lighthouse/blob/master/types/lhr.d.ts
      // use results.report for the HTML/JSON/CSV output as a string
      // use results.artifacts for the trace/screenshots/other specific case you need (rarer)
      return chrome.kill().then(() => results.lhr)
    });
  });
}

However, I would like to check multiple urls. This will require creating the browser window with chromeLauncher.launch , executing the lighthouse function on each url, and then finally calling chrome.kill() on the window, before returning the results.

Initially, I tried solving this using Promise.all

  return chromeLauncher.launch({chromeFlags: opts.chromeFlags}).then(chrome => {
    opts.port = chrome.port;

    const checks = urls.map((url) => {
      console.log('checking', url)
      return lighthouse(url, opts, config)
    })

    return Promise.all(checks).then((results) => {
      return chrome.kill().then(() => results)
    })
  });

but since each call to lighthouse uses a shared resource they must wait for the previous call to return before continuing, whereas Promise.all tries make all the lighthouse calls in parallel, and then resolves. This doesn't work with the single shared browser resource.

Then I tried experimenting with using a reducer, with the chrome launcher promise as the initial value.

  const checks = urls.reduce((previous, url) => {
    return previous.then((previousValue) => {
      return lighthouse(url, opts, config)
    })
  }, chromeLauncher.launch({chromeFlags: opts.chromeFlags}))

  checks
    .then((results) => {
      console.log('something')
      return chrome.kill()
    })
    .then((results) => results.lhr)

but this also doesn't work when calling the lighthouse function, but I think this is the correct approach to calling a chain of promises synchronously.

Does anyone have any suggestions where I'm going wrong, or what else I could try?

With async await:

const lighthouse = require('lighthouse');
const chromeLauncher = require('chrome-launcher');

async function launchChromeAndRunLighthouse(urls, opts, config = null) {
    const chrome = await chromeLauncher.launch({chromeFlags: opts.chromeFlags})
    opts.port = chrome.port;
    const results = [];
    for (const url of urls) {
         results.push(await lighthouse(url, opts, config));
    }
    await chrome.kill();
    return results;
}

const opts = {
    chromeFlags: ['--show-paint-rects']
};

// Usage:
const urls = ['http://www.google.de', 'http://www.heise.de'];
launchChromeAndRunLighthouse(urls, opts).then(results => {
    console.log(results);
});

Classic:

const lighthouse = require('lighthouse');
const chromeLauncher = require('chrome-launcher');

function launchChromeAndRunLighthouse(urls, opts, config = null) {
    return chromeLauncher.launch({chromeFlags: opts.chromeFlags}).then(chrome => {
        opts.port = chrome.port;

        const results = [];
        return urls.reduce((p, url) => {
            return p.then(() => {
                return lighthouse(url, opts, config).then((data) => {
                    results.push(data);
                    return results;
                });
            });
        }, Promise.resolve())
            .then(() => chrome.kill())
            .then(() => results)
    });
}

const opts = {
    chromeFlags: ['--show-paint-rects']
};

// Usage:
const urls = ['http://www.google.de', 'http://www.heise.de'];
launchChromeAndRunLighthouse(urls, opts).then(results => {
    console.log(results);
})

Or using a library like Bluebird:

const Promise = require('bluebird');
const lighthouse = require('lighthouse');
const chromeLauncher = require('chrome-launcher');

function launchChromeAndRunLighthouse(urls, opts, config = null) {
    return chromeLauncher.launch({chromeFlags: opts.chromeFlags}).then(chrome => {
        opts.port = chrome.port;

        return Promise.mapSeries(urls, url => lighthouse(url, opts, config))
            .then((results) => chrome.kill().then(() => results))
    });
}

const opts = {
    chromeFlags: ['--show-paint-rects']
};

// Usage:
const urls = ['http://www.google.de', 'http://www.heise.de'];
launchChromeAndRunLighthouse(urls, opts).then(results => {
    console.log(results);
});

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

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