简体   繁体   English

JavaScript / Promise-定义Promise链接之间的超时

[英]JavaScript/Promise - Define timeout between promises chaining

I am using cheeriojs to scrap a site, i need to emit a lot of requests on several url parameters. 我正在使用cheeriojs抓取网站,我需要在几个url参数上发出很多请求。

minimal code: 最小代码:

const rp = require('request-promise');
const cheerio = require('cheerio');

[1, 2, 3].forEach(element => {
  url = `https://stackoverflow.com/q=${element}`
  rp(url)
    .then((html) => {
      // Logic code
   })
})

I would like to set a timeout between each request, how can we define it? 我想在每个请求之间设置一个超时,我们如何定义它?

I think the most readable approach would be to use an async function and promise-ified timeout. 我认为最易读的方法是使用异步功能和承诺承诺的超时。

function sleep(millis) {
  return new Promise(resolve => setTimeout(resolve, millis));
}
async function process(list) {
  for (const item of list) {
    const html = await rp(`https://stackoverflow.com/q=${item}`);
    ... do stuff
    await sleep(1000);
  }
}

Currently all the requests are essentially made in parallel. 当前,所有请求基本上都是并行进行的。 Before you can add a delay between them you have to execute them in sequence. 您必须先按顺序执行它们,然后才能在它们之间添加延迟。 You can do that by chaining promises. 您可以通过链接诺言来做到这一点。 This is easy to do with .reduce : 使用.reduce很容易做到:

const rp = require('request-promise');
const cheerio = require('cheerio');

[1, 2, 3].reduce((p, element) => {
  url = `https://stackoverflow.com/q=${element}`
  return p
    .then(() => rp(url))
    .then((html) => {
      // Logic code
    });
}, Promise.resolve())

This builds a chain that is equivalent to 这建立了一条相当于

rp(url1)
  .then(html => ...)
  .then(() => rp(url1))
  .then(html => ...)
  .then(() => rp(url2))
  .then(html => ...)

To add a delay, we define a function that returns a function that returns a promises that resolves after x milliseconds via setTimeout : 为了增加延迟,我们定义了一个函数,该函数返回一个函数,该函数返回通过setTimeout在x毫秒后解析的promise:

function wait(x) {
  return () => new Promise(resolve => setTimeout(resolve, x));
}

Now we can add that to our chain (I'm replacing rp with something runnable here): 现在我们可以将其添加到我们的链中(我在这里用可运行的东西替换rp ):

 function wait(x) { return () => new Promise(resolve => setTimeout(resolve, x)); } [1, 2, 3].reduce((p, element) => { const url = `https://stackoverflow.com/q=${element}` return p .then(() => Promise.resolve(url)) .then((html) => { console.log(`Fetched ${html}`); }) .then(wait(2000)); }, Promise.resolve()) 

You can use the index argument of forEach as multiplier for timeout delay 您可以将forEach的index参数用作超时延迟的乘数

const delay = 1000

[1, 2, 3].forEach((element, i) => {
    url = `https://stackoverflow.com/q=${element}`
    setTimeout(() => {
       rp(url)
           .then((html) => {
            // Logic code
           })
    }, i * delay);

})

If you want to use a forEach statement, use my first code. 如果要使用forEach语句,请使用我的第一个代码。 If it doesn't matter to you, see my second (simpler) working example, based on @JFord's answer. 如果对您来说没有关系,请参阅基于@JFord的答案的第二个(简单的)工作示例。

RunKit demo with forEach forEach的RunKit演示

RunKit demo with for item of list for item of list RunKit演示

Note: the code has been fixed to work properly 注意:该代码已修复,可以正常工作

forEach example forEach示例

const rp = require('request-promise')
const cheerio = require('cheerio')

function sleep(ms) {
  return new Promise(resolve => setTimeout(resolve, ms))
}

async function forEachAsync(arr, fn) {
  for (var i = 0; i < arr.length; i++) {
    await fn(arr[i])
  }
}

async function fetchUrls() {
  await forEachAsync([55505362, 55505363, 55505364], async element => {
    await sleep(2000)
    console.log('been 2000 seconds')
    var url = `https://stackoverflow.com/questions/${element}`
    await rp(url)
      .then(html => {
        console.log(html)
      })
      .catch(function(e) {
        console.log(e.message) // "oh, no!"
      })
  })
}

fetchUrls()

for item of list example for item of list示例的for item of list

This is a working example, based on the answer by @JFord but additionally handling errors. 这是一个有效的示例,基于@JFord的回答,但还处理错误。

const rp = require('request-promise')
const cheerio = require('cheerio')

function sleep(ms) {
  return new Promise(resolve => setTimeout(resolve, ms))
}

async function fetchUrls(list) {
  for (const item of list) {
    const html = await rp(`https://stackoverflow.com/q=${item}`).catch(function(e) {
        console.log(e.message) // There's an error
    })
    console.log("html: " + html)
    await sleep(2000);
  }
}

fetchUrls([1,2,3])

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

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