【问题标题】:async/await: resolving Promise.all() in batches of Nasync/await:分批解析 Promise.all() N
【发布时间】:2018-06-02 08:05:10
【问题描述】:

tl;dr 在 Promises 方面需要一些帮助。

这里有一个小爬虫功能,它的核心是:

function request(method, url) {
    return new Promise(function (resolve, reject) {
        var xhr = new XMLHttpRequest();
        xhr.open(method, url);
        xhr.onload = resolve;
        xhr.onerror = reject;
        xhr.send();
    });
}

我还有一个相当大的配置文件列表要解决:

const profiles = ['http://www.somesite.com/profile/1',
'http://www.somesite.com/profile/2'] // 100+ more

我如何分批处理它们,比如一次 5 个?

到目前为止,这是我的思考过程:

  1. 使用_.chunk()分割成N个块
  2. 等待 Promise.all() 解析所述块

这是我目前所拥有的:

async function processProfileBatch(batchOfUrls) {
    let promises = [];

    // Populate promises
    for (let i = 0; i < batchOfUrls.length; i++) {
        let url = batchOfUrls[i]
        promises.push(request('GET', url))
    }

    // Wait for .all to resolve
    return await Promise.all(promises)
}
const profileBatches = _.chunk(profileLinks, 3)
for (let i = 0; i < profileBatches.length; i++) {
        let processedBatch = await processProfileBatch(profileBatches[i])
        console.log(new Date(), 'processedBatch', processedBatch);
    }

不幸的是,这只是返回ProgressEvents;经检查,其中包含的 xhr 已将 .responseText 设置为“”,即使 readyState 为 4:

【问题讨论】:

  • 你可以使用 fetch 代替 XMLHttpRequst
  • @HRM 无法使用 fetch - 目标站点对 cookie/会话或其他内容很敏感。
  • 获取ProgressEvent对于收听onload来说是正常的,你可以做xhr.onload = () =&gt; resolve(xhr)之类的来获得不同的结果值
  • 如果responseText 为空,那听起来很可能是SOP issue
  • 您是否尝试使用包含凭据的获取? fetch(url, { method: 'GET', credentials: 'include' })

标签: javascript async-await xmlhttprequest es6-promise


【解决方案1】:

分块的问题是您有 x 个活动请求,然后等待所有请求完成以开始下一个 x 数量的请求。

使用throttle,您可以持续激活 x 数量的请求,直到所有请求都完成。

//lib comes from: https://github.com/amsterdamharu/lib/blob/master/src/index.js
const lib = require("lib");

function processProfileBatch(batchOfUrls){
  const max10 = lib.throttle(10)
  return Promise.all(
    batchOfUrls.map(
      url=>
        max10(url=>request('GET', url))(url)
    )
  )
};

如果您希望限制每个周期的连接数(例如每秒 2 个)而不是限制活动连接数,您可以使用 throttlePeriod:

twoPerSecond = lib.throttlePeriod(2,1000);
... other code
twoPerSecond(url=>request('GET', url))(url)

您可能还不想在一个拒绝时丢弃所有已解决的请求。对被拒绝的请求使用特殊的解析值,您可以将被拒绝的请求与已解决的请求分开:

//lib comes from: https://github.com/amsterdamharu/lib/blob/master/src/index.js
const lib = require("lib");

function processProfileBatch(batchOfUrls){
  const max10 = lib.throttle(10)
  return Promise.all(
    batchOfUrls.map(
      url=>
        max10(url=>request('GET', url))(url)
        .catch(err=>new lib.Fail([err,url]))
    )
  )
};

processProfileBatch(urls)
.then(//this does not reject because rejects are caught and return Fail object
  result=>{
    const successes = results.filter(lib.isNotFail);
    const failed = results.filter(lib.isFail);
  }
)

【讨论】:

  • 这很简洁,让我沉思一下。然而,我注意到,如果我只是再等一会儿,所有的请求都会很好地解决。换句话说,看起来Promise.all 由于某些莫名其妙的原因解析得比它应该的要快。
猜你喜欢
  • 2018-06-27
  • 2022-11-23
  • 2020-09-03
  • 1970-01-01
  • 2018-12-26
  • 1970-01-01
  • 2020-03-25
  • 2018-12-01
  • 1970-01-01
相关资源
最近更新 更多