假设您有一个包含值列表的数组/对象。让我们说一下mysql命令或url或filespaths。现在,您想迭代所有这些并在每个条目上执行一些代码。
for(let i = 0; i < urls.length; i++){
doSthWith(urls[i]);
}
到目前为止没有问题。但是现在让我们说每个函数都有一个回调并且需要上次执行的结果。例如您从一个网站请求某些内容,并且您希望将此请求的结果用于以下某个请求。
for(let i = 0; i < urls.length; i++){
if(resultOfLastIteration.successful){ //or some other result besides the last one
doSthWith(urls[i]);
}
}
现在让我们说网址的长度(或类似的)超过100.这就是为什么你正常使用一个循环所以你不需要写100次相同的功能。这也意味着Promises也不会做这个伎俩(除了我不知道欺骗技巧),因为你有同样的问题:
doSthWith(urls[0]).then(...
doSthWith(urls[1]).then(... //either put them inside each other
).then(...
doSthWith(urls[i]) //or in sequence
...
).catch(err){...}
无论哪种方式,我都没有看到使用循环的方法。
我找到的一种方式,但并不是真的&#34;好&#34;是使用包&#34; wait.for&#34;(https://www.npmjs.com/package/wait.for)。但是,这个包很棘手的是每次你想使用wait.for时发射光纤:
//somewhere you use the function in a fiber Context
wait.for(loopedExecutionOfUrls, urls);
//function declaration
function loopedExecutionOfUrls(urls, cb){
//variables:
for(let i = 0; i < urls.length; i++){
if(someTempResultVar[i-1] === true){
someTempResultVar = wait.for(doSthWith,urls[i]);
} else if(...){...}
}
}
但是我不确定这种方法是否真的很好,除了你总是要检查你是否已经将整个事物包装在一个光纤中,因此对于每个具有带回调功能的循环的函数。因此,您有3个级别:lauchFiber级别,wait.for(loopedFunction)级别和wait.for回调函数级别。 (希望我认为这是可以理解的)
所以我的问题是:你们有一个很好的方法,你可以循环抛出回调函数,并可以随时使用那些结果吗?
好=易于使用,阅读,高效,不递归,......
(对不起,如果这个问题很愚蠢,但我真的遇到了这个异步编程的问题)
答案 0 :(得分:3)
如果你想在做同样的事情之前等待doSthWith
但是使用nex网址,你必须链接你的承诺,你可以使用array.prototype.reduce
来做到这一点:
urls = ["aaa", "bbb", "ccc", "ddd"];
urls.reduce((lastPromise, url) => lastPromise.then((resultOfPreviousPromise) => {
console.log("Result of previous request: ", resultOfPreviousPromise); // <-- Result of the previous request that you can use for the next request
return doSthWith(url);
}), Promise.resolve());
function doSthWith(arg) { // Simulate the doSthWith promise
console.log("do something with: ", arg);
return new Promise(resolve => {
setTimeout(() => resolve("result of " + arg), 2000);
});
}
&#13;
答案 1 :(得分:2)
使用async,特别是async.each
:
const async = require('async');
function doSthWith(url, cb) {
console.log('doing something with ' + url);
setTimeout(() => cb(), 2000);
}
const urls = ['https://stackoverflow.com/', 'https://phihag.de/'];
async.each(urls, doSthWith, (err) => {
if (err) {
// In practice, likely a callback or throw here
console.error(err);
} else {
console.log('done!');
}
});
如果您对结果感兴趣,请使用async.map
。
答案 2 :(得分:0)
当我需要循环承诺时,我会使用我方便的花花公子ploop功能。这是一个例子:
// Function that returns a promise
var searchForNumber = function(number) {
return new Promise(function(resolve, reject) {
setTimeout(function() {
var min = 1;
var max = 10;
var val = Math.floor(Math.random()*(max-min+1)+min);
console.log('Value is: ' + val.toString());
return resolve(val);
}, 1000);
});
};
// fn : function that should return a promise.
// args : the arguments that should be passed to fn.
// donefn : function that should check the result of the promise
// and return true to indicate whether ploop should stop or not.
var ploop = function(fn, args, donefn) {
return Promise.resolve(true)
.then(function() {
return(fn.apply(null, args));
})
.then(function(result) {
var finished = donefn(result);
if(finished === true){
return result;
} else {
return ploop(fn, args, donefn);
}
});
};
var searchFor = 4;
var donefn = function(result) {
return result === searchFor;
};
console.log('Searching for: ' + searchFor);
ploop(searchForNumber, [searchFor], donefn)
.then(function(val) {
console.log('Finally found! ' + val.toString());
process.exit(0);
})
.catch(function(err) {
process.exit(1);
});