I have found out how to pass additional variables to the promise .then function by using Promise.all. But how can I pass these variables to the .catch function?
My abstracted code:
// 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
});
}
This is just a very abstract code to explain that I need variable i also to be available in the .catch function.
How to achieve this?
EDIT:
In response to the answers and my comments to them, here is my complete code with using let
in the for loop. I think now it is a very neat use of promises to search different search engines in parallel and use the first useful hit.
I use Promise.any
- an inverted Promise.all
- to get the first resolved promise from an array of promises. And I use the for loop to prepare the promises for the Promise.any. nzbDonkeySettings is a global variable with my settings:
// 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"));
});
});
}