Javascript 延迟承诺。所有顺序
我正在从外部API获取数据,在某些端点上出现以下错误:Javascript 延迟承诺。所有顺序,javascript,node.js,Javascript,Node.js,我正在从外部API获取数据,在某些端点上出现以下错误: reason: getaddrinfo EAI_AGAIN [the url]:443 我只能假设API的所有者有某种速率限制,因为所有的抓取都是一次性完成的,所以一些URL会被阻止 所以我试图在每次抓取之间设置一个延迟,但没有运气。以下是我的尝试: class someClass { ... dealyedFetch(url) { return new Promise ((resolve, reject) => {
reason: getaddrinfo EAI_AGAIN [the url]:443
我只能假设API的所有者有某种速率限制,因为所有的抓取都是一次性完成的,所以一些URL会被阻止
所以我试图在每次抓取之间设置一个延迟,但没有运气。以下是我的尝试:
class someClass {
...
dealyedFetch(url) {
return new Promise ((resolve, reject) => {
setTimeout(() => {
fetch(url, (err, res) => {
if (err) return reject(err)
return resolve(res)
})
}, 5000)
})
}
fetchUrls() {
return Promise.all(
this.urlList.map(url => {
return this.dealyedFetch(url)
.then(/* macht krieg */)
.catch(err => ({err, url}))
})
)
}
}
上述方法不起作用,它似乎一次触发了所有抓取操作。请告诉我。对于您的
urlist
,您同时做出了一系列承诺。每一个承诺都会等待5秒,然后它们都会同时执行(5秒后)
有关以串行方式运行承诺的规范SO post,请参见以下内容:
从该职位上解除:
function runSerial(tasks) {
var result = Promise.resolve();
tasks.forEach(task => {
result = result.then(() => task());
});
return result;
}
然后你会用runSerial替换你的promise.all来消耗它。您可以通过一次释放5个url来进一步自定义逻辑,然后以串行方式运行批处理,但这应该可以让您开始了。您一次延迟了所有批处理,因此它们都等待5000毫秒,然后一起触发 如果你想把它们串起来,你需要让它们的延迟时间逐渐延长。您可以使用
映射中的索引将延迟(例如500毫秒)乘以:
delayedFetch(url, delay) { // ***
return new Promise ((resolve, reject) => {
setTimeout(() => {
fetch(url, (err, res) => {
if (err) return reject(err)
return resolve(res)
})
}, delay) // ***
})
}
fetchUrls() {
return Promise.all(
this.urlList.map((url, i) => { // ***
return this.delayedFetch(url, i * 500) // ***
.then(/* macht krieg */)
.catch(err => ({err, url}))
})
)
}
第一个请求根本不会延迟,第二个请求是1*500=500ms,第三个请求是2*500=1000ms,以此类推
旁注:在上文中,我修复了名称delayedFetch
中的一个拼写错误(问题中dealyedFetch
,a
和l
被颠倒)
边注2:上面提到了你实际提出的问题,但是你可以考虑一次做一个,在这种情况下告诉你如何做到这一点。或者对未完成的部分设置并发限制,在这种情况下会有所帮助。仅仅一个固定的延迟可能不是最好的解决方案。可能会将延迟传递给delayedFetch
,并基于数组中的索引,如this.urlist.map((url,index)=>…delayedFetch(url,index*1000)…
以不同的方式偏移它们中的每一个。承诺。all
不能使任何事情连续,它只是等待。您正在触发所有的delayedFetch
调用(它们都使用相同的超时!)在map
呼叫的同一瞬间。如果那里的答案回答了这个问题,正确的做法是投票以重复的方式结束(可能还带有注释),而不是发布一个回答说“去看看这个其他答案”(无论是否引用部分答案).@T.J.Crowder在我看来,作为一个复制品结束并不能解决问题的根本问题。我知道,复制品对我如何做X很有帮助,而不是我在做Y,但它不起作用,所以有一个明确的政策,什么是复制品,什么不是复制品:如果答案回答了问题,那么它就是复制品。(现在,在我看来,这个问题在这里不会是一个双重问题,因为它回答了一个不同的问题——尽管它可能指向一个不同的方法来解决这个OP的根本问题。)您认为在这里使用异步函数更有意义吗?@S.Schenk:async
函数只是返回承诺的函数的语法糖。不过,它确实是非常有用的糖,如果您可以使用它,那就太好了。(如果您使用的是任何最新版本的节点,您也可以这样做。)例如,如果您想一次只执行一个请求(例如,按顺序)使用async
/await
它是const results=[];for(this.urlsist的const url){results.push(await fetch(url));}
函数中的async
函数。一次具有多个并发限制会更复杂。