我已经找到了如何使用Promise.all将其他变量传递给promise .then函数。但是如何将这些变量传递给.catch函数呢?
我抽象的代码:
// global variable url
var url = [url1, url2, url3];
for (i = 0, i < url.length, i++) {
// run a promise.all to pass some additional variables to the next .then
Promise.all([xhr(url[i]), i]).then(function([response, i]) {
// do something with the response and variable i
alert(url[i] + " responded: " + response);
}).catch(function(e) {
// I need variable i also to be available here!
alert(url[i] + " failed to respond");
});
}
function xhr(url) {
return new Promise(function(resolve, reject) {
// xmlhttprequest with url
// resolves with the response
// or rejects with an error message
});
}
这只是一个非常抽象的代码来解释我需要变量我也可以在.catch函数中使用。
怎么做到这一点?
编辑:
在回答答案和我对他们的评论时,这是我在for循环中使用let
的完整代码。我认为现在使用promises非常巧妙地并行搜索不同的搜索引擎并使用第一个有用的命中。我使用Promise.any
- 一个倒置的Promise.all
- 从一系列承诺中获得第一个解决的承诺。我使用for循环来为Promise.any准备承诺。 nzbDonkeySettings是我的设置的全局变量:
// modifiy the promise prototype to add function "any"
// from https://stackoverflow.com/questions/39940152/get-first-fulfilled-promise
Promise.prototype.invert = function() {
return new Promise((res, rej) => this.then(rej, res));
};
Promise.any = function(ps) {
return Promise.all(ps.map((p) => p.invert())).invert();
};
// function to search and download the nzb file
function searchNZB(nzbHeader) {
// prepare the promises for all search engines
var getNZB = []; // the promise array
for (let i = 0; i < nzbDonkeySettings.searchengines.length; i++) {
if (nzbDonkeySettings.searchengines[i].active) { // but only for "active" search engines
getNZB[i] = new Promise(function(resolve, reject) {
// first search for the nzb header
var nzbSearchURL = nzbDonkeySettings.searchengines[i].searchURL.replace(/%s/, encodeURI(nzbHeader));
var options = {
"url": nzbSearchURL,
"responseType": "text",
"timeout": 20000
};
xhr(options).then(function(response) {
// if we have a response, check if we have a result
var re = new RegExp(nzbDonkeySettings.searchengines[i].searchPattern, "i");
if (re.test(response)) {
// if we have a result, generate the url for the nzb file
var nzbDownloadURL = nzbDonkeySettings.searchengines[i].downloadURL.replace(/%s/, response.match(re)[nzbDonkeySettings.searchengines[i].searchGroup]);
// and download the nzb file
var options = {
"url": nzbDownloadURL,
"responseType": "text",
"timeout": 120000
};
xhr(options).then(function(response) {
// if we have a response, check if it is a nzb file
if (response.match(/<nzb.*>/i)) {
// if it is a nzb file, resolve with the nzb file
resolve(response);
} else {
// if it is not a nzb file, reject
reject(new Error(nzbDonkeySettings.searchengines[i].name + ": " + "the downloaded file is not a valid nzb file"));
}
}).catch(function(e) {
// if the download failed, reject
reject(new Error(nzbDonkeySettings.searchengines[i].name + ": " + "an error occurred while trying to download the nzb file"));
});
} else {
// if we have no result, reject
reject(new Error(nzbDonkeySettings.searchengines[i].name + ": " + "no nzb file found"));
}
}).catch(function(e) {
// if we have no response, reject
reject(new Error(nzbDonkeySettings.searchengines[i].name + ": " + "an error occurred while searching for the nzb file"));
});
});
}
}
// return a promise
return new Promise(function(resolve, reject) {
// lets "race" the promises and get the result from the first resolved promise
Promise.any(getNZB).then(function(nzbFile) {
// resolve with the nzbFile
resolve(nzbFile);
}).catch(function(e) {
// if we have no results, reject
reject(new Error("no search engine returned any usable result"));
});
});
}
像这样使用let
:
for (let i = 0, i < url.length, i++) {
let
为i
循环的每次迭代创建一个单独的变量for
,所以即使for
循环在你的xhr()
请求运行时继续运行,循环的每次调用仍然可以访问它自己的i
副本。
此外,没有理由在循环内部使用Promise.all()
。对于循环的每次迭代,您只有一个xhr()
承诺:
// global variable url
var url = [url1, url2, url3];
for (i = 0, i < url.length, i++) {
// run a promise.all to pass some additional variables to the next .then
xhr(url[i]).then(function([response, i]) {
// do something with the response and variable i
alert(url[i] + " responded: " + response);
}).catch(function(e) {
// I need variable i also to be available here!
alert(url[i] + " failed to respond");
});
}
现在,如果您想知道所有xhr()
调用何时完成,您可以这样做:
// global variable url
var url = [url1, url2, url3];
Promise.all(url.map((u, i) => {
return xhr(u).catch(err => {
console.log(u + " failed to respond");
return null;
});
})).then(results => {
console.log(results);
}).catch(err => {
console.log(err);
});
我并不是真的喜欢以这种方式向promises添加额外数据的想法。您可以简单地重构代码以使用forEach
和范围来实现所需的效果:
const urls = [url1, url2, url3];
urls.forEach(url => {
xhr(url)
.then(response => alert(url + "responded: " + response))
.catch(() => alert(url + "failed to respond"))
})
这可能是解决问题的另一种方法;
var apiCall = url => new Promise((v,x) => Math.random() < 0.67 ? v(`some data from ${url}`) : x("Oops..!")),
urls = ["url1","url2","url3"],
ps = urls.map((u,i) => apiCall(u).then(v => console.log(`Result from API call # ${i+1} is ${v}`))
.catch(e => console.log(`Received Error ${e} from API call ${i+1}`)));