Promise.all():在所有 Promises 被解决和/或拒绝后返回结果 [英] Promise.all(): Return a result after all Promises are resolved and/or rejected
问题描述
我在使用此代码时遇到问题:
//var env_array = ["env1", "env2", "env3", "env4"];Promise.all(env_array.map(function(env) {返回 device_get_env(env).catch(function(err) { return err });})).then(函数(数据){控制台日志(数据);数据.forEach(功能(条目){console.log(entry.data.connected);});}).catch(函数(数据){控制台日志(数据);});函数 device_get_env(env) {var env = ...;var device_id = ...;返回 get_token_data(env, 0).then(function(data) {var url_base = ... ;返回 $.ajax({网址:url_base,方法:获取",数据类型:'json',标头:{授权:data.token_type + " " + data.access_token}});});}函数 get_token_data(env, auth) {var client_id = env_tokens[env].client_id;var client_secret = env_tokens[env].client_secret;var 观众 = auth == 1 ?"https://" + env + ".xxxx.com/api/v2/" : "yyyy.com";返回 $.ajax({url: "https://" + env + ".xxxx.com/oauth/token",方法:POST",数据: {client_id":client_id,client_secret":client_secret,观众":观众,"grant_type": "client_credentials"},数据类型:'json'});}
基本上我需要遍历 env_array
并在我的一些环境中找到设备项.
device_get_env() 返回 AJAX 调用,可能是成功/200 或错误/404.
因此,除非所有承诺都得到解决,否则我的 Promises.all 不会返回.
我一直在研究如何克服这个问题.
一直在尝试实施此解决方案:https://stackoverflow.com/a/30378082/1913289,但我此处出现此错误:
TypeError: device_get_env(env).catch 不是函数.(在'device_get_env(env).catch(function(err) {return err})'中,'device_get_env(env).catch'是未定义的)
有什么办法可以用我的代码解决这个问题吗?
UPD:@Bergi 建议的实现
function get_token_data(env, auth) {var client_id = env_tokens[env].client_id;var client_secret = env_tokens[env].client_secret;var 观众 = auth == 1 ?"https://" + env + ".xxxx.com/api/v2/" : "yyyy.com";返回 Promise.resolve($.ajax({url: "https://" + env + ".xxxx.com/oauth/token",方法:POST",数据: {client_id":client_id,client_secret":client_secret,观众":观众,"grant_type": "client_credentials"},数据类型:'json'}))}函数 device_get_env(env) {var env = ...;var device_id = ...;返回 get_token_data(env, 0).then(function(data) {var url_base = ... ;返回 Promise.resolve($.ajax({网址:url_base,方法:获取",数据类型:'json',标头:{ 授权:data.token_type + " " + data.access_token }}))});}Promise.all(env_array.map(function(env) {返回 device_get_env(env).then(null, function(err) { return err });})).then(函数(数据){控制台日志(数据);}).catch(函数(数据){控制台日志(数据);});
UPD1:
Promise.all(env_array.map(function(env) {返回 device_get_env(env).catch(function(err) {return err} );})).then(函数(数据){控制台日志(数据);}).catch(函数(数据){控制台日志(数据);});
最终更新:我使用了获取:https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API/Using_Fetch#Supplying_request_options
替换我的 AJAX http 请求.
<块引用>即使响应是 HTTP 404 或 500,从 fetch() 返回的 Promise 也不会拒绝 HTTP 错误状态.相反,它将正常解析 >(将 ok 状态设置为 false),并且只会网络故障时拒绝 > 或者如果有任何原因阻止请求完成.
Promise.all 示例:
Promise.all(env_array.map(function(env) {返回 device_get_env(env);})).then(函数(数据){控制台日志(数据);}).catch(函数(数据){控制台日志(数据);});
获取示例:
function get_token_data(env, auth) {var client_id = env_tokens[租户].client_id;var client_secret = env_tokens[env].client_secret;var 观众 = auth == 1 ?"https://" + env + ".xxxx.com/api/v2/" : "yyyy.com";var url_base = "https://" + env + ".xxxx.com/oauth/token";var myInit = {方法:'POST',模式:'cors',数据类型:'json',缓存:'默认',数据: {client_id":client_id,client_secret":client_secret,观众":观众,"grant_type": "client_credentials"}};返回获取(url_base,myInit);}
你没有在代码中的某处返回 Promise 的第一个线索是你的错误:
TypeError: device_get_env(env).catch 不是函数.
错误告诉您,您正试图在一个没有 catch()
方法的对象上调用 .catch()
(即使AJAX 对象在技术上是thenable"的,早期版本的 jQuery 没有 catch()
方法.所以一种解决方案是升级你的 jQuery1 版本.>
您的特殊问题是您没有从 get_token_data()
函数中的 AJAX 调用中获得您期望的 ES6 承诺,而是返回了一个 jQuery 承诺.
因此,如果您通过 get_token_data()
中的 Promise.resolve()
将 jQuery 承诺包装/转换为 catch()
-able ES6 Promise代码> 那么你应该在正确的轨道上:
function get_token_data(env, auth) {var client_id = env_tokens[env].client_id;var client_secret = env_tokens[env].client_secret;var 观众 = auth == 1 ?"https://" + env + ".xxxx.com/api/v2/" : "yyyy.com";var ajaxOptions = {url: "https://" + env + ".xxxx.com/oauth/token",方法:POST",数据类型:'json',数据: {client_id":client_id,client_secret":client_secret,观众":观众,"grant_type": "client_credentials"}};//这里是魔法发生的地方//jQuery Promise 变成可捕获"的 Promise返回 Promise.resolve($.ajax(ajaxOptions));};
我无法测试此代码,因此您可能需要对其进行调整,但这是基本思路.
希望这会有所帮助!
1感谢@Bergi 提醒我这一点.
I am having trouble with this code:
// var env_array = ["env1", "env2", "env3", "env4"];
Promise.all(env_array.map(function(env) {
return device_get_env(env).catch(function(err) { return err });
})).then(function(data) {
console.log(data);
data.forEach(function(entry) {
console.log(entry.data.connected);
});
}).catch(function(data) {
console.log(data);
});
function device_get_env(env) {
var env = ...;
var device_id = ...;
return get_token_data(env, 0).then(function(data) {
var url_base = ... ;
return $.ajax({
url: url_base,
method: "GET",
dataType: 'json',
headers: {Authorization: data.token_type + " " + data.access_token}
});
});
}
function get_token_data(env, auth) {
var client_id = env_tokens[env].client_id;
var client_secret = env_tokens[env].client_secret;
var audience = auth == 1 ? "https://" + env + ".xxxx.com/api/v2/" : "yyyy.com";
return $.ajax({
url: "https://" + env + ".xxxx.com/oauth/token",
method: "POST",
data: {
"client_id": client_id,
"client_secret": client_secret,
"audience": audience,
"grant_type": "client_credentials"
},
dataType: 'json'
});
}
Basically I need to iterate over env_array
and find device items in some of my environments.
device_get_env() returns AJAX call, which could be a success/200 or error/404.
Thus my Promises.all won't return unless all promises are resolved.
I've been digging how to overcome this.
Been trying to implement this solution: https://stackoverflow.com/a/30378082/1913289, but I'm having this error here:
TypeError: device_get_env(env).catch is not a function. (In 'device_get_env(env).catch(function(err) {return err} )', 'device_get_env(env).catch' is undefined)
Any way to solve this with my code here?
UPD: implementation suggested by @Bergi
function get_token_data(env, auth) {
var client_id = env_tokens[env].client_id;
var client_secret = env_tokens[env].client_secret;
var audience = auth == 1 ? "https://" + env + ".xxxx.com/api/v2/" : "yyyy.com";
return Promise.resolve(
$.ajax({
url: "https://" + env + ".xxxx.com/oauth/token",
method: "POST",
data: {
"client_id": client_id,
"client_secret": client_secret,
"audience": audience,
"grant_type": "client_credentials"
},
dataType: 'json'
})
)
}
function device_get_env(env) {
var env = ...;
var device_id = ...;
return get_token_data(env, 0).then(function(data) {
var url_base = ... ;
return Promise.resolve(
$.ajax({
url: url_base,
method: "GET",
dataType: 'json',
headers: { Authorization: data.token_type + " " + data.access_token }
})
)
});
}
Promise.all(env_array.map(function(env) {
return device_get_env(env).then(null, function(err) { return err });
})).then(function(data) {
console.log(data);
}).catch(function(data) {
console.log(data);
});
UPD1:
Promise.all(env_array.map(function(env) {
return device_get_env(env).catch(function(err) {return err} );
})).then(function(data) {
console.log(data);
}).catch(function(data) {
console.log(data);
});
FINAL UPD: I used fetch: https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API/Using_Fetch#Supplying_request_options
to replace my AJAX http requests.
The Promise returned from fetch() won’t reject on HTTP error status even > if the response is an HTTP 404 or 500. Instead, it will resolve normally > (with ok status set to false), and it will only reject on network failure > or if anything prevented the request from completing.
Promise.all example:
Promise.all(env_array.map(function(env) {
return device_get_env(env);
})).then(function(data) {
console.log(data);
}).catch(function(data) {
console.log(data);
});
fetch example:
function get_token_data(env, auth) {
var client_id = env_tokens[tenant].client_id;
var client_secret = env_tokens[env].client_secret;
var audience = auth == 1 ? "https://" + env + ".xxxx.com/api/v2/" : "yyyy.com";
var url_base = "https://" + env + ".xxxx.com/oauth/token";
var myInit = {
method: 'POST',
mode: 'cors',
dataType: 'json',
cache: 'default',
data: {
"client_id": client_id,
"client_secret": client_secret,
"audience": audience,
"grant_type": "client_credentials"
}
};
return fetch(url_base, myInit);
}
Your first clue that you aren't returning a Promise somewhere in your code is in your error:
TypeError: device_get_env(env).catch is not a function.
The error is telling you that you are trying to call .catch()
on an object that doesn't have a catch()
method on it (even though AJAX objects are technically "thenable", earlier versions of jQuery don't have a catch()
method. So one solution is to upgrade your version of jQuery1.
Your particular problem is that you are not getting the ES6 Promise you expect from the AJAX call in your get_token_data()
function, instead, you are returning a jQuery Promise.
So if you wrap/cast the jQuery promise to a catch()
-able ES6 Promise via Promise.resolve()
in get_token_data()
then you should be on the right track:
function get_token_data(env, auth) {
var client_id = env_tokens[env].client_id;
var client_secret = env_tokens[env].client_secret;
var audience = auth == 1 ? "https://" + env + ".xxxx.com/api/v2/" : "yyyy.com";
var ajaxOptions = {
url: "https://" + env + ".xxxx.com/oauth/token",
method: "POST",
dataType: 'json',
data: {
"client_id": client_id,
"client_secret": client_secret,
"audience": audience,
"grant_type": "client_credentials"
}
};
// here is where the magic happens to cast your
// jQuery Promise into a 'catchable' Promise
return Promise.resolve($.ajax(ajaxOptions));
};
I can't test this code, so you might need to tweak it, but that is the basic idea.
Hope this helps!
1 Thanks to @Bergi for reminding me of this.
这篇关于Promise.all():在所有 Promises 被解决和/或拒绝后返回结果的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!