我试图了解如何在javascript中使用承诺,但我的脚本从未执行"Then"



我有一个脚本,可以设置一定数量的用户名,然后为每个用户名运行一个获取请求来抽搐 API 端点以获取每个用户的 Json 信息。然后我想把这些信息放到一个数组中供以后使用,发现我必须使用承诺,而我以前从未使用过。我通读了这个:https://developers.google.com/web/fundamentals/primers/promises 和这个:https://github.com/domenic/promises-unwrapping/blob/master/docs/states-and-fates.md,我在这里读了Felix Kling的回应:如何从异步调用返回响应?而且我仍然难以准确理解什么去哪里,什么做什么。这是我到目前为止所拥有的:

var users = ["ESL_SC2", "OgamingSC2", "cretetion", "freecodecamp", "storbeck", "habathcx", "RobotCaleb", "noobs2ninjas"]
var arr = [];
var headers = {
'Client-Id': 'xxxxxxxxxxx'
}
var promise = new Promise(function usersJson(callback) {
for (i = 0; i < users.length; i++) {
var url = "https://api.twitch.tv/helix/users?login=" + users[i];
fetch(url, {headers})
.then(function(response) {
return response.json();
})
.then(function(myJson) {
arr.push(myJson);
})
callback(arr);
};
})
promise.then(function(result) {
console.log(result);
})

据我了解,这应该通过每个端点运行,将数据推送到我的数组变量,然后一旦整个事情"实现",它应该用我的 promise 变量调用"然后"的东西来控制台.log我的数组。我得到的只是一个空白数组,我真的不确定我做错了什么。如果我在 arr.push(json) 之后放入控制台.log(arr),我会返回 8 个数组,每个数组都附加了下一个响应正文,所以我知道调用正在工作,我只是无法让它粘在我的数组中。

一种可以帮助你在承诺中思考更多问题的方法是,一旦你进入了承诺之地,你只能在承诺之地时获得数据。在您的示例中,您从fetch承诺中获取数据。这意味着您只能从fetch承诺内部使用该数据。请注意:

.then(function(myJson) {
arr.push(myJson);
})
callback(arr);

您构建阵列的arr.push在承诺内部,但callback(arr)在外部。由于您不再在承诺范围内,因此您无法获取数据。

在您的情况下,您还希望同时等待多个承诺,因为您一次从多个来源获取数据。最好的方法是使用Promise.all和一系列承诺:

const promise = Promise.all(users.map(user => {
const url = `https://api.twitch.tv/helix/users?login=${user}`;
return fetch(url, { headers })
.then(function(response) {
return response.json();
});
}));

现在,一旦所有承诺得到解决,promise.then将允许您访问一系列数据。你不需要推送到另一个数组,.map会为你创建承诺数组。


您也可以使用async/await语法编写此语法,该语法更容易推理且可能更干净。

const promise = Promise.all(users.map(async user => {
const url = `https://api.twitch.tv/helix/users?login=${user}`;
const response = await fetch(url, { headers });
return response.json();
}));

顺便说一下,您只需在使用本身不实现承诺的库时才需要使用new Promisefetch已经返回了一个 Promise,所以当你调用它时,你已经得到了一个 Promise,所以没有必要用new Promise包装它。

要获得所需的结果,可以将代码重写为

const users = ["ESL_SC2", "OgamingSC2", "cretetion", "freecodecamp", "storbeck", "habathcx", "RobotCaleb", "noobs2ninjas"]
const headers = {
'Client-Id': 'xxxxxxxxxxx'
}
Promise.all(users.map(user => fetch(`https://api.twitch.tv/helix/users?login=${user}`, {headers}).then(response => response.json())))
.then(result => console.log(result));

更具可读性

const users = ["ESL_SC2", "OgamingSC2", "cretetion", "freecodecamp", "storbeck", "habathcx", "RobotCaleb", "noobs2ninjas"]
const headers = {
'Client-Id': 'xxxxxxxxxxx'
}
const mapper = user => 
fetch(`https://api.twitch.tv/helix/users?login=${user}`, {headers})
.then(response => response.json());
const promises = users.map(mapper);
Promise.all(promises)
.then(result => console.log(result));

最后,由于您的原始代码显示不了解ES2015 +。

var users = ["ESL_SC2", "OgamingSC2", "cretetion", "freecodecamp", "storbeck", "habathcx", "RobotCaleb", "noobs2ninjas"];
var headers = {
'Client-Id': 'xxxxxxxxxxx'
};
var mapper = function mapper(user) {
return fetch("https://api.twitch.tv/helix/users?login=" + user, { headers: headers }).then(function (response) {
return response.json();
});
};
var promises = users.map(mapper);
Promise.all(promises).then(function (result) {
return console.log(result);
});

改为执行以下操作:

const runTheTrap = async function(users){
const ret = [];
for (i = 0; i < users.length; i++) {
var url = "https://api.twitch.tv/helix/users?login=" + users[i];
await fetch(url, {headers})
.then(function(r) {
return r.json().then(v => ret.push(v));
});
};
return ret;
}

runTheTrap(users).then(function(result) {
console.log(result);
});

但坦率地说,异步/等待有点"迟到",我更喜欢这种方法:

const runTheTrap = function(users){
return users.map(function(u){
var url = "https://api.twitch.tv/helix/users?login=" + u;
return fetch(url, {headers}).then(r => {
return r.json();
});
}

Promise.all(runTheTrap(users)).then(function(vals){
// you have your vals
});

最新更新