【问题标题】:I'm trying to understand how to use promises in javascript, but my script never executes the "Then"我试图了解如何在 javascript 中使用 Promise,但我的脚本从不执行“Then”
【发布时间】:2018-09-29 08:31:26
【问题描述】:

我有一个脚本,它设置了一组用户名,然后为每个用户名运行一个 fetch 请求以 twitches API 端点获取每个用户的 Json 信息。然后我想将这些信息放入一个数组中以备后用,并发现我必须使用我以前从未使用过的 Promise。我通读了这个:https://developers.google.com/web/fundamentals/primers/promises 和这个:https://github.com/domenic/promises-unwrapping/blob/master/docs/states-and-fates.md,我在这里阅读了 Felix Kling 的回复:How do I return the response from an asynchronous call?,我仍然无法准确理解什么去哪里以及做什么。这是我到目前为止所拥有的:

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 变量调用“then”事情来控制台。记录我的阵列。我得到的只是一个空白数组,我真的不确定我做错了什么。如果我将 console.log(arr) 放在 arr.push(json) 之后,我会返回 8 个数组,每个数组都附加了下一个响应主体,所以我知道调用正在工作,但我无法得到它坚持我的阵列。

【问题讨论】:

  • 为什么在 forEach 可以使用时使用 for 循环?
  • 你也没有承诺任何回报。
  • 不,实际上你的callback(arr); 应该在第二个then 内,而且每次获取都是一个承诺,所以一旦你解决了第一个问题,它就会触发callback(arr) 并退出
  • 我只是将它切换到 forEach,但这并不能解决我的问题。据我所知,使用 forEach 还是 for 循环并不重要。
  • 你在 promise 执行之前调用了callback(arr);

标签: javascript arrays json promise


【解决方案1】:

改为这样做:

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);
});

但坦率地说 async/await 有点“迟到”,我更喜欢这种方法:

     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
       });

【讨论】:

  • I prefer this approach - 除非,您返回的是一个数组......并且一个数组没有 .then 方法,因此,您首选的方法是有缺陷的
  • 是啊,让我修复
【解决方案2】:

为了得到你想要的结果,你的代码可以重写为

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);
});

【讨论】:

  • 我会说这是一个公平的交易
  • 好吧,你的意见更好,@AlexanderMills。也许您已经解释得更多,但是您的非 async/await 代码非常错误……Array#map 没有返回承诺,因此runTheTrap(users).then 会抱怨.then 不是函数:p
  • 那是真的我现在修好了很多吗?不要让我撤回我的赞成票大声笑
【解决方案3】:

一种可以帮助您在 Promise 中进行更多思考的方法是,一旦您处于 Promise-land 中,您只能在您仍在 Promise-land 中时获取数据。在您的示例中,您从 fetch 承诺中获取数据。这意味着您只能在 fetch 承诺中使用该数据。注意这里:

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

您在其中构建数组的arr.pushinside promise,但callback(arr)outside。由于您不再处于承诺范围内,因此您无法获取数据。

在您的情况下,您还希望同时等待多个承诺,因为您一次从多个来源获取数据。最好的方法是使用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 包装它。

【讨论】:

  • 这是一个了不起的解释,非常感谢。我能够让 console.log 使用这些信息,我应该能够从这里继续前进。非常感谢。
猜你喜欢
  • 2021-04-24
  • 1970-01-01
  • 2018-01-08
  • 2016-06-01
  • 2018-12-31
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-01-31
相关资源
最近更新 更多