【问题标题】:JavaScript Fetch & Push Array returns “undefined”JavaScript Fetch & Push 数组返回“未定义”
【发布时间】:2020-05-16 14:59:56
【问题描述】:

我正在用纯 JavaScript 编写应用程序。我遇到了一个问题,当将 150 个对象放入数组时,数组中的一些对象是未定义的。当我在将对象推入数组之前对其进行 console.log 时,它们很好,并且所有 150 个对象都会显示。但是当我将它们推送到数组时,有些是未定义的,有些确实被成功推送了。我的理解是,这可能是序列问题的时间问题,对于某些对象,它们可能尚未完全获取,但推送到数组已经发生。有没有办法确保它们是有序的,以便只有在完全成功获取对象后才会推送数组?感谢您的帮助。

const fetchPokemon = function () {

  const pokemons = [];

  for ( let i=1; i <= 150; i++) {
      const url = `https://pokeapi.co/api/v2/pokemon/${i}`;
      fetch(url).then( res => { return res.json(); })
      .then( data => {
          const pokemon = {
              name: data.name,
              id: data.id          
          };
          pokemons.push(pokemon);
          console.log(pokemon);
          console.log(pokemons[i]);
      });
  }
};

fetchPokemon();

输出 - 根据未定义的数组元素,页面的不同重新加载会有所不同

app.js:17 undefined
app.js:16 {name: "dragonite", id: 149}
app.js:17 undefined
app.js:16 {name: "mewtwo", id: 150}
app.js:17 undefined
app.js:16 {name: "kingler", id: 99}
app.js:17 {name: "electrode", id: 101}
app.js:16 {name: "aerodactyl", id: 142}
app.js:17 {name: "zapdos", id: 145}
app.js:16 {name: "dratini", id: 147}
app.js:17 {name: "kingler", id: 99}

【问题讨论】:

  • 我认为这不是时机,您正在使用应在 fetch 任务完成时运行的承诺。您正在对 pokemon api 进行 150 次独立调用,这些 id 是否存在 pokemon?是否有一条路线可以让您一次性检索列表而不是 150 次?需要打很多电话
  • 在 pokemons[i] 中,我会在执行该行时引用循环变量值,但数组可能还没有获取和推送那么多值。我建议不要依赖循环内回调中的i 变量。附注 - 看看 async/await 而不是回调。
  • 要查看被推入数组的内容,您可以这样做console.log(pokemons[pokemons.length-1]);

标签: javascript arrays asynchronous fetch


【解决方案1】:

您的问题是,虽然 fetch 对于每个单独的请求都是异步的,但您在所有 150 个请求完成之前返回。在下面的 for 循环中,不是链接到请求并立即推送,而是将 promise 推送到 requests 数组中。然后,在循环之后,您返回一个 Promise,一旦请求了所有 150 个 Promise 并将其推送到 pokemons 数组中,该 Promise 就会解析。

const fetchPokemon = function() {
  const pokemons = [];
  const requests = [];
  for (let i = 1; i <= 150; i++) {
    const url = `https://pokeapi.co/api/v2/pokemon/${i}`;
    const prom = fetch(url).then((r) => r.json());

    requests.push(prom);
  }
  return new Promise((resolve) => {
    Promise.all(requests)
      .then((proms) =>
        proms.forEach((p) => pokemons.push({
          name: p.name,
          id: p.id
        }))
      )
      .then(() => resolve(pokemons));
  });
};

fetchPokemon().then(console.log);

【讨论】:

  • 感谢您的回答 aprouja1。非常感谢。
  • 嗨@aprouja1,为了便于理解,如果我想使用函数表示法而不是箭头函数,我正在尝试将您的代码分成单独的更小的块: const pokemons = []; const fetchPokemon = function () { const requests = []; for (let i = 1; i https://pokeapi.co/api/v2/pokemon/${i}; //const prom = fetch(url).then((r) => r.json()); fetch(url) .then(function (r) { return r.json(); }) .then(function (data){ requests.push(data); }); //requests.push(舞会); }
  • 但是我无法让代码工作,因为当获得 .json 承诺对象时,能够将其推送到 requests 数组。你能指出我正确的方向吗?非常感谢。
【解决方案2】:

在此处发布此内容,作为使用 async/await 的更简单、更简洁和最佳的示例。
并行提取,具有迭代第一个顺序就绪提取响应的特性。即:如果前 25 次获取完成,它将按顺序迭代前 25 次,而无需等待后 125 次。

const fetchPokemon = async function() {
  const pokemons = [];

  for (let i = 1; i <= 150; i++) {
    const url = `https://pokeapi.co/api/v2/pokemon/${i}`;
    const data = fetch(url).then(res => res.json())
                           .then( ({name, id}) => ({name, id}) );
    pokemons.push(data);
  }
  for await (const pokemon of pokemons) {
    console.log(pokemon);
  }
  // or if you need to use the index:
  for (let i = 0; i < pokemons.length; i++) {
    console.log(await pokemon[i]);
  }
};

fetchPokemon();

【讨论】:

  • 感谢您的回答 user120242。真的很感激。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多