【问题标题】:Waiting for multiple (async) API calls to complete等待多个(异步)API 调用完成
【发布时间】:2017-11-30 12:52:22
【问题描述】:

非常像一个 nodejs 菜鸟,试图理解 Promise、await、async。我向你保证我做了尽职调查研究(花了一整天的时间来研究下面的代码。我仍然不能完全确定一切都应该是这样,并且找不到具有完全相同的东西(或足够接近)的参考我在这里尝试做的事情。

感谢您的帮助。

一般结构:

function msg() 正在等待 4 个函数完成,这 4 个 api 调用(代码只显示了一个):function redditPromise()。

redditPromise() 调用异步函数 redditGet() -> 这是调用 reddit API 并同时将 API 数据保存到数据库的函数。 (函数saveToDb())

var nodeSocialApi = require('node-social-api');
var Socstat = require('../proxy').Socstat;

exports.index = function (req, res, next) {

/* SAVES DATA TO MONGODB */
function saveToDb(website,total) {
     //** Start : NewAndSave
     Socstat.newAndSave(website, total, "random", function (err, socstat) { // newAndSave -> proxy/socstat.js
          if (err) {
               return next(err);
          }
     });
     //** End : NewAndSave
}
/* END SAVES DATA TO MONGODB */

/* GET DATA FROM REDDIT API */
const reddit = new nodeSocialApi.Reddit(); // no auth needed

async function redditGet() {
   let result;

   await reddit.get('r/about/about.json')
   .then((data) => {

   // callback value for promise
   result = data.data.subscribers;
   saveToDb("reddit",result);

   }) // end then
   .catch(err => console.log(err));

   return result;
}
/* END : GET DATA FROM REDDIT API */

/* REDDIT PROMISE (all the others look the same) */
function redditPromise() {
     return new Promise(resolve => {
         resolve(redditGet());
     });
}
/* END : REDDIT PROMISE (all the others look the same) */

/* ONE FUNCTION THAT WAITS FOR ALL PROMISED FUNCTIONS */
async function msg() {
  const [a, b, c,d] = await Promise.all([githubPromise(), twitterPromise(), redditPromise(), facebookPromise()]);

  console.log(a + " " + b + " " + c  + d);
}
/* END: ONE FUNCTION THAT WAITS FOR ALL PROMISED FUNCTIONS */

msg();

}; // END exports

【问题讨论】:

  • 您在运行代码时是否遇到错误?
  • 代码有问题吗?看起来代码会起作用。也许最好将其发布在Code Review
  • 为什么twitterPromise 调用redditGet?真的,这整个函数是多余的——你可以(也应该)直接调用redditGet,得到一个结果完全相同的promise。
  • 您的saveToDb 函数没有返回承诺。它确实应该,以便您可以在需要时等待它。 (如果没有,你should at least handle errors from it)。
  • 代码没有问题,但这并不意味着我不期望将来会出现任何问题。例如,正如您所指出的,如果数据库调用失败怎么办。非常感谢您抽出宝贵时间。我将再花几个小时使用您提供的链接来编写 DB 调用的承诺(以及 @raghu 和 @HMR 提出的其他优化)

标签: javascript node.js asynchronous promise


【解决方案1】:

您发布的代码中唯一可用的功能是 twitterPromise,我建议作为 Bergi 来返回承诺:

const saveToDb = (website,total) =>
  //** Start : NewAndSave
  new Promise(
    (resolve,reject) =>
      Socstat.newAndSave(
        website, 
        total, 
        "random", 
        (err, socstat) => // newAndSave -> proxy/socstat.js
          (err)
            ? reject(err)
            : resolve(socstat)
      )
  );

//redditGet does not deal with errors, 
//  the caller has to deal with the errors (rejected promise)
const redditGet = async () => {
  const data = await reddit.get('r/about/about.json');
  await saveToDb("reddit",data.data.subscribers);
  //just return a promise:
  return data.data.subscribers;
};

//facebookGet does not deal with errors, 
//  the caller has to deal with the errors (rejected promise)
const facebookGet = async () => {  
  const response = await facebook.get('1118720888180564/fields=fan_count');
  console.log(response.fan_count);
  const dbResult = await saveToDb("facebook",response.fan_count)
  console.log("db entry succesfull -> " + dbResult);
  return response.fan_count;
};
//msg does not deal with errors, the caller of msg does
const msg = () =>
  Promise.all([githubPromise(), twitterPromise(), redditPromise(), facebookPromise()])

//calling msg and dealing with the error and result
msg()
.then(
  results =>
    console.log("got resullts",results)
  ,reject =>
    console.error("rejected",reject)
);

如果你理解 promise,你可以 investigate async await,它对于习惯于同步脚本的人来说具有更简单的语法(try catch),但最终它会返回一个 promise。

【讨论】:

  • @RahulPrajapati 我在谈论异步时并不是指库,而是用于异步返回函数的 es6 async await 语法:developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/…
  • 哇,HMR,Bergi 和@Rahul。非常感谢。在这里我可以学到很多新东西。例如,在 _ => data.data.subscribers 中使用下划线 您的解决方案肯定比我的更优雅,更简洁。接受答案。
  • @RahulPrajapati 不,那个库对承诺毫无用处。
  • @HMR 当然你不能没有承诺,但它是 then 回调的一个很好的语法糖。
  • @Bergi 是的,同意。语法确实看起来更好,所以我更新了答案。由于语法糖会导致函数返回承诺,我认为最好不要引入 async await 语法,直到 OP 更好地理解承诺。通常不要在显示 OP 对 Promise 不够了解的 Promise 问题上使用该语法。
【解决方案2】:

希望我把这个写成一个新的“答案”是正确的。基本上只是想表明我没有把这里的帮助视为理所当然。一直在努力支付我的会费。

基于 cmets (@HMR),我尝试将我的一些函数转换为 async/await。 我想我不确定两件事: 1) 我的最终返回 response.fan_count 是在等待 saveToDb 完成吗?我尝试将 return response.fan_count 放在 saveToDb.then 承诺中,但这不起作用。 2) 我应该将我的 saveToDb.then() 结构也转换为 async/await 吗?

const facebookGet = async () => {  

facebookGet ()  {
    //just return a promise:
    let response; // outside to be able to return in the end
    try {
         response = await facebook.get('1118720888180564/fields=fan_count');
         console.log(response.fan_count);
         saveToDb("facebook",response.fan_count)
         .then(dbResult => {
              console.log("db entry succesfull -> " + dbResult); // returns the database object
         })
         .catch(err => console.error(err));
    }
    catch (err) {
         console.log("fetch failed ", err);
    }
return response.fan_count;
}

回答我自己的问题 1)

在“saveToDb”之前放置一个“return”...允许我在“.then”中写入“return response.fan_count”

【讨论】:

  • 2) 如果你不想等待数据库保存(目前),你不应该在这里使用await
  • 更新了我的答案以使用异步等待语法。我假设 facebookGet 和 redditGet 不想处理错误,但调用者会。由于您使用的是Promise.all,有些可能会解决,有些可能会拒绝,如果您想在某些功能失败时撤消操作,那么它会变得有点复杂。您必须编写撤消函数并查看已解决的问题(等待全部解决或失败)。 Promise.all 不会拒绝所有值,但是一旦失败,就会调用拒绝处理程序。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2017-01-23
  • 1970-01-01
  • 2012-05-04
  • 2019-10-30
  • 1970-01-01
  • 1970-01-01
  • 2011-02-15
相关资源
最近更新 更多