Javascript 当任何承诺完成时,是否有可能履行承诺?
我有一个URL数组,我想对其进行web请求。我想用承诺异步调用web请求,并为任何已完成的承诺继续链。现在我正在做Javascript 当任何承诺完成时,是否有可能履行承诺?,javascript,node.js,promise,Javascript,Node.js,Promise,我有一个URL数组,我想对其进行web请求。我想用承诺异步调用web请求,并为任何已完成的承诺继续链。现在我正在做 .then((lines) => { var tasks = []; lines.forEach((line) => { tasks.push(() => { return rp(BASE_URL + line); }); }); return Promise.map(tasks, (job) => { ret
.then((lines) => {
var tasks = [];
lines.forEach((line) => {
tasks.push(() => { return rp(BASE_URL + line); });
});
return Promise.map(tasks, (job) => { return job(); }, { concurrency: 5 });
})
.then((info) => {
inspect('info: ' + info);
})
但是,它的问题是在调用下一个.then()
我想知道是否可以为任何已解决的任务调用.then(),并继续该链
我猜我可以做如下事情:
.then((lines) => {
lines.forEach((line) => {
rp(BASE_URL + line)
.then((info) => {
inspect(info);
});
});
})
.then((a) => {
// all requests have been made
})
.then((lines) => {
lines.forEach((line) => {
yield return rp(BASE_URL + line);
});
})
.then((info) => {
// will call inspect on each yield
inspect('info: ' + info);
})
当我嵌套.then()时,这就开始了类似于回调地狱的事情,在其他一些遇到相同问题的情况下,可能需要这样做
我要找的是:
.then((lines) => {
lines.forEach((line) => {
rp(BASE_URL + line)
.then((info) => {
inspect(info);
});
});
})
.then((a) => {
// all requests have been made
})
.then((lines) => {
lines.forEach((line) => {
yield return rp(BASE_URL + line);
});
})
.then((info) => {
// will call inspect on each yield
inspect('info: ' + info);
})
您可以使用和稍微整理一下回调地狱版本,以降低一些嵌套级别
.map((line) => {
return rp(BASE_URL + line);
}, {concurrency: 5})
.each((info) =>
inspect(info);
})
.then((a) => {
// all requests have been made
})
请注意,这与您的版本有点不同。。每个
回调将仅在完成所有请求后运行。转向基于流的处理模型可以避免这种情况
我有一个URL数组,我想对其进行web请求。我想用承诺异步调用web请求,并为任何已完成的承诺继续链
您将如何在同步代码中实现它?承诺模拟同步代码
您可以将处理整个单个请求的逻辑放在一个函数中,然后map
:
.map(processLine);
function processLine(line) {
return rp(BASE_URL + line).then(inspect);
}
重要的是要理解,这不是一个工件,而是promise代理模型的核心属性。承诺表示回调之类的单个任务,而不是事件发射器之类的多个任务
你可能想考虑一个可观察的或反应性更强的系统,如果你感兴趣的话,你也可以使用异步迭代器(有效的你的“期望的”语法)。我使用可观测数据,因为它们在这里更容易使用,但两者都可以工作:
new Observable(obs => {
let reqs = lines.map(x => rp(BASE_URL + line));
let counter = reqs.length;
for(let req of reqs) {
req.then(v => {
obs.onNext(v);
if(!--counter) obs.onCompleted();
});
}
}).map(u => { // every new value is available here
console.log(u); // use flatMap and return a promise if you want
// promise like chaining behavior
});
您的“回调地狱”根本不是地狱,而是一种并行运行多个异步链的优雅方式,这是同步所无法比拟的
但是,您的版本中缺少了一个关键的返回
,这就是为什么我开始喜欢无{}
的箭头函数的隐式返回,而您忽略了映射
在es6中,我会:
.then(lines => Promise.all(lines.map(line =>
rp(BASE_URL + line).then(info => inspect(info)))));
})
.then(allInspectResults => {
// all requests and inspections have been made
})
.catch(e => console.error(e));
你可以实现你自己的承诺。可以将其命名为
PromiseGroup
或PromiseList
。然后,该类型的对象将表示任意数量的承诺。然后你可以写,例如,PromiseGroup(行)。然后(行=>rp(BASE_URL+line))。然后(info=>inspect('info:'+info))
你的“回调地狱”版本是正确的。承诺使您不必为多个回调保持嵌套缩进级别,但如果您需要在链的“顶层”执行操作,嵌套承诺很好在看到Benjamin的解决方案后,我不得不说,可观测值看起来是一个很好的解决方案。因此,也许你可以使用一个名为arrayObservable
的函数或类似的函数来将行
转换为一个可观察的。如果(!--counter)或者如果(--counter==0)
,这不应该是吗?很好的捕获,应该是可观察的。从(行)。映射(x=>rp(BASE\u URL+line)).flatMap(x=>x)
而不是那种明确的结构。这似乎是我试图避免的基本问题。如果rp()需要很长时间,但我需要它的结果,然后发出另一个阻塞调用,然后我需要它的结果发出另一个阻塞调用,依此类推,那么我必须等待a全部完成后才能调用b,然后等待b全部完成后才能调用c,等等。。我需要能够在a的某个项目完成时调用b(a)。rp
是异步的,对吗?那么就没有阻塞了。假设有5行,那么rp()
将被一个接一个地调用5次,希望每个调用立即返回,然后inspect
调用将立即执行,直到您有5个承诺。从现在开始,等待所有5件事情完成的唯一事情就是allInspectResults
。5个inspect
调用中的每一个都只取决于相应rp
调用的承诺,它们被调用的顺序取决于哪个rp
调用首先完成。rp
调用的异步部分并行运行。