Javascript:如何将其他变量传递给promise .catch函数

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Javascript:如何将其他变量传递给promise .catch函数相关的知识,希望对你有一定的参考价值。

我已经找到了如何使用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++) {

leti循环的每次迭代创建一个单独的变量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}`)));

以上是关于Javascript:如何将其他变量传递给promise .catch函数的主要内容,如果未能解决你的问题,请参考以下文章

如何将 Django 变量传递给 Javascript

如何将变量(laravel)传递给javascript?

如何从 Blade 格式 HTML 将 PHP 变量传递给 JavaScript 函数

如何将javascript变量传递给django模板标签

如何将 JavaScript 变量传递给 jQuery ajax 请求

如何将javascript变量传递给数据库