【问题标题】:How to fetch() multiple resources?如何获取()多个资源?
【发布时间】:2021-06-08 05:39:57
【问题描述】:

我有一个指向 json 资源的地址列表。我想下载这些文件以便在以后的处理中使用它们。

我有这段使用 fetch() 方法的代码:

let urlList = [
         "https://url.to/resource1.json",
         "https://url.to/resource2.json"
       ];
  let promiseList = [];
  let jsonBaseList = [];

  urlList.forEach (function (url, i) {
    promiseList.push (
      fetch(url).then (function (res) {
        jsonBaseList[i] = res.json();
      })
    );
  });

  Promise
    .all(promiseList)
    .then (function () {
      console.log('All done.');
    })

console.log('jsonBaseList: ', jsonBaseList)

因此,jsonBaseList 包含promises 的列表。

但我只想要一个 json 对象列表。

有可能吗?

【问题讨论】:

  • 你错过了一步fetch(url).then(response => response.json()).then(data=> jsonBaseList[i] = data);
  • 旁注:但我只想要一个 json 对象列表。" JSON 是一种用于数据交换的文本表示法(More here.) 如果你正在处理 JavaScript 源代码,而不是处理 string,你不是在处理 JSON。

标签: javascript html json promise fetch


【解决方案1】:

您应该返回 res.json() 并在下一个 .then 中使用解析后的值,因为 Response.json() 返回另一个承诺。

let urlList = [
         "https://url.to/resource1.json",
         "https://url.to/resource2.json"
       ];
  let promiseList = [];
  let jsonBaseList = [];

  urlList.forEach (function (url, i) {
    promiseList.push (
      fetch(url).then (function (res) {
        return res.json();
      }).then(function (res) {
        jsonBaseList[i] = res;
      })
    );
  });

  Promise
    .all(promiseList)
    .then (function () {
      console.log('All done.');
    })

console.log('jsonBaseList: ', jsonBaseList)

更新:我刚刚编辑了您当前的代码以使其正常工作。但你可以写得更好:

let urlList = [
  "https://url.to/resource1.json",
  "https://url.to/resource2.json"
];

let jsonBaseList = [];

const promiseList = urlList.map((url) => {
  return fetch(url)
      .then(response => response.json())
})

Promise.all(promiseList).then(values => {
  jsonBaseList = values;
  console.log('All done.');
})

console.log('jsonBaseList: ', jsonBaseList)

更新:代码末尾的console.log 将输出一个空数组,因为promise 在当前脚本运行后异步运行。所以你可以:

  • 放入.then
  • 或将其放入链式.then
  • 或使用async/await 语法(编写承诺的更简洁的方式)
(async function() {
  let urlList = [
    "https://url.to/resource1.json",
    "https://url.to/resource2.json"
  ];

  const promiseList = urlList.map((url) => {
    return fetch(url)
        .then(response => response.json())
  })

  const jsonBaseList = await Promise.all(promiseList)
  console.log('All done.');
  console.log('jsonBaseList: ', jsonBaseList)
})()

【讨论】:

  • @ArtemArkhipov - 同意。虽然是穷人,但您可能想在最后删除console.log,或者至少解释为什么它总是会记录[]。 :-)
【解决方案2】:

因此,jsonBaseList 包含一个承诺列表。

是的,因为res.json() 返回了一个承诺。但是,当您显示 console.log 时,jsonBaseList 将是 [],因为该代码在任何承诺解决之前运行。

对于jsonBaseList 的代码在承诺解决后运行的代码中包含值的最小更改是:

// ...
// ...
let urlList = [
    "https://url.to/resource1.json",
    "https://url.to/resource2.json"
];
let promiseList = [];
let jsonBaseList = [];

urlList.forEach (function (url, i) {
    promiseList.push (
        fetch(url) .then (function (res) {
            return res.json();
        })
        .then (function (value) {     // ***
            jsonBaseList[i] = value;  // *** This is what I added
        })                            // ***
    );
});

Promise
    .all(promiseList)
    .then (function () {
        console.log('All done.');     // *** Use `jsonBaseList` here
    })

// Removed the `console.log` here that would have logged `[]`

但是它可以简单得多;以上所有内容都可以替换为:

let urlList = [
    "https://url.to/resource1.json",
    "https://url.to/resource2.json"
];
Promise.all(urlList.map(url => fetch(url).then(response => response.json())))
.then(jsonBaseList => {
    // ...use `jsonBaseList` here...
})
.catch(error => {
    // ...handle/report error here...
});

请注意,您在Promise.all 上的then 处理程序中使用了jsonBaseList。我不会在更广泛的范围内声明它,因为在调用该处理程序之前它不会被填充(这就是你最后的console.log 将始终记录[] 的原因)。如果您在更广泛的范围内声明它,您可能会在它可用之前尝试使用它(如问题代码中所示)。

但如果想在更广泛的范围内填写,并且您意识到要等到以后才能填写,请添加:

let jsonBaseList = []; // *** Not filled in until the promises settle!

然后改变

.then(jsonBaseList => {
    // ...use `jsonBaseList` here...
})

.then(list => {
    jsonBaseList = list;
})

(或使用constjsonBaseList.push(...list)。)


旁注:您可能想要处理 HTTP 请求失败的可能性(即使网络请求成功 - 这是我写的关于 herefetch API 中的枪,它不会拒绝 HTTP失败,只是网络故障)。所以:

let urlList = [
    "https://url.to/resource1.json",
    "https://url.to/resource2.json"
];
Promise.all(
    urlList.map(
        url => fetch(url).then(response => {
            if (response.ok) {
                throw new Error(`HTTP error ${response.status}`);
            }
            return response.json();
        })
    )
)
.then(jsonBaseList => {
    // ...use `jsonBaseList` here...
})
.catch(error => {
    // ...handle/report error here...
});

【讨论】:

  • 我认为我们需要坚持问题本身,而不是教额外的东西,因为我们不知道提问者的水平。我们可以只提一个指向该额外信息的链接作为建议。
  • @pooria - 我不确定你指的是什么。以上主要是对实际问题的非常直接的回答。
  • @mplungjan - ...但无论如何我都试图澄清。 :-D
  • @mplungjan - jsonBaseList 数组是来自Promise.all 的承诺的实现值。我建议的替换不会零碎地填充数组,它会将完整的填充数组传递给履行处理程序。当您执行Promise.all([a, b, c]) 时,Promise.all 的 promise 的履行值是 abc 的履行值(以相同的顺序)。
  • 是的,我现在明白了。 jsonBaseList 现在只是 Promise.all 的“外部”then 中的一个命名变量 - 与之前的脚本相比,这是令人困惑的地方
【解决方案3】:

你需要先等待你的承诺完成

await Promise.all(promiseList)
console.log('jsonBaseList: ', jsonBaseList)

【讨论】:

  • 如果 OP 可以使用await,那么代码可以而且应该完全重构。 (编辑:不是我的反对票)
  • 它不能解决问题。通过使用 await 您可以避免使用 then 但问题仍然存在,因为原因是作者没有正确使用 res.json() 方法。查看@pooria 的正确答案
猜你喜欢
  • 2020-03-01
  • 2014-10-05
  • 2018-01-13
  • 1970-01-01
  • 1970-01-01
  • 2016-12-04
  • 2015-10-09
  • 2013-06-07
  • 2015-01-30
相关资源
最近更新 更多