如何将 promise 返回的数据推送到数组中?

How to push the data returned by a promise into an array?

我正在打电话给 Udemy API。为了进行同时调用,我使用了一个循环。通过这样做,我会自动增加页码并尝试从每一页获取数据并将其存储到一个数组中,以便我可以将所有数据写入 json 格式的单个文件中。但我得到的只是一个空数组。如何访问 promise 返回的值并将其存储到 doc.table 数组中?

我的代码:

const fetch=require("node-fetch");
const fs=require("fs");
let doc={};
doc.table=[];

for(let i=1;i<=10;i++){

fetch('https://www.udemy.com/api-2.0/courses/ page='+i+'&page_size=10&client_id=${client_id}&client_secret=${client_secret},{
      method:'GET',
      body:null,
      headers:{authorization: ${auth_code}}
      })
      .then(res=>res.json())
      .then(json=>doc.table.push(json))
};


fs.writeFile("UDEMY.json",JSON.stringify(doc),function(err){
    if(err) throw err;
    console.log("Complete");
});

您可以尝试检查当前循环索引,并在最后一个 Promise fullfillment 中写入您的文件:

const fetch = require('node-fetch');
const fs = require('fs');

let url;
let counter = 10;
const doc = {
  table: []
};

for (let i = 1; i <= 10; i++) {
  url = `https://www.udemy.com/api-2.0/courses/page=${i}&page_size=10&client_id=${client_id}&client_secret=${client_secret}`;
  fetch(url, {
    method: 'GET',
    body: null,
    headers: {
      authorization: auth_code
    }
  })
  .then(res => res.json())
  .then(json => {
    // next line will not guarantee the order of pages
    // doc.table.push(json);
    // so we can use current loop index and counter
    doc.table[i] = json;
    // when counter is 0 we can write a file 
    if (!--counter) {
      fs.writeFile('UDEMY.json', JSON.stringify(doc), function(err) {
        if (err) {
          throw err;
        }
        console.log("Complete");
      });
    }
  })
};

我还修复了您 URL 的模板字符串中的小错误...

我建议使用 await 这样您的 for 循环将在每次迭代时暂停:

const fetch = require("node-fetch");
const fsp = require("fs").promises;

let doc = { table: []};

async function run() {
    for (let i = 1; i <= 10; i++) {

        let data = await fetch(`https://www.udemy.com/api-2.0/courses?page=${i}&page_size=10&client_id=${client_id}&client_secret=${client_secret}`,{
              method:'GET',
              body:null,
              headers:{authorization: auth_code}
        }).then(res=>res.json());

        doc.table.push(data);
    }

    await fsp.writeFile("UDEMY.json",JSON.stringify(doc));
    console.log("done");
}

run().catch(err => {
    console.log(err);
});

另一种可能性是 运行 并行处理所有请求,并使用 Promise.all() 知道它们何时全部完成。这两种解决方案的关键是使用 fetch() returns 的承诺来控制事情何时完成。

如果你真的想 运行 它们并行并且你确定你的目标主机会允许它,你可以这样做:

const fetch = require("node-fetch");
const fsp = require("fs").promises;

let doc = { table: []};

function run() {
    let promises = [];
    for (let i = 1; i <= 10; i++) {

        promises.push(fetch(`https://www.udemy.com/api-2.0/courses?page=${i}&page_size=10&client_id=${client_id}&client_secret=${client_secret}`},{
              method:'GET',
              body:null,
              headers:{authorization: ${auth_code}}
        }).then(res=>res.json()));

    }
    return Promise.all(promises).then(data => {
        doc.table = data;
        return fsp.writeFile("UDEMY.json",JSON.stringify(doc));
    });

}

run().then(() => {
    console.log('done');
}).catch(err => {
    console.log(err);
});

并且,如果您想要某种级别的并行请求,但又想限制并行请求的数量,您可以使用 mapConcurrent() 描述的

如果顺序很重要,您还可以将承诺保存在一个数组中,并在每个承诺完成后访问它们

const promises = []
promises.push(...)
Promise.all(promises).then(data -> ....)

data 现在将是一个数组,其中包含各个承诺的结果。您可以随意合并或处理它们。 请注意,只有在所有先前的承诺都已解决后,上述功能才会解决。