【问题标题】:Await inside loop before move to another iteration在移动到另一个迭代之前等待内部循环
【发布时间】:2019-08-27 11:14:12
【问题描述】:

我正在尝试使用函数通过 API 发送消息。当函数履行职责时,它返回一个值 messageLodId 并且需要在主循环中的 Attendence 处更新。但是当我执行此代码时,值未定义。

有两个问题:

1) 结构对吗?

2) 如果是,请提供此问题的答案。

//Posting SMS 
router.post('/sms/', async function(req, res) {
    let attendenceRecordId = parseInt(req.body.attendenceRecordId);
    let result = await AttendenceRecord.findOne({where: {id: attendenceRecordId }, include: [ {model: Attendence, include: [{model: Student}
    ]}, {
        model: Class
    }], order: [['date', 'DESC']]});

    if(!result) {
        res.sendStatus(404); 
    }

    for await (let attendence of result.attendences){

        let messageLogId = await sendSMS(attendence); 

        console.log("Message ID: ", messageLogId); 
        Attendence.update(
            { smsLogId: messageLogId },
            { where: { id: attendence.id  } }
          ); 
    }

    AttendenceRecord.update(
        { isMessageSent:true },
        { where: { id: result.id } }
      ); 

    res.send({messageSent: true});

});


这里是函数定义。现在我只返回 1 个。

在现实世界中,URL 返回一个代码。

async function sendSMS(attendence){

    //console.log(target);


    setTimeout(function(){
        let message = `Respected Parent, your son/daughter ${attendence.student.name} is absent on ${attendence.date}`;
        let messageURL = encodeURI(message); 
        let api = 'SOME VALUE';
        let phone = attendence.student.fphone.substring(1, 11); 
        let target = `http://BASE_URL/api.php?key=${api}&receiver=92${phone}&sender=DigitalPGS&msgdata=${messageURL}`; 

        return 1; 
    }, 2000); 


}

【问题讨论】:

  • sendSMS 不返回任何内容,所以你会得到undefined
  • 函数sendSMS 不返回任何内容。只有setTimeout 的回调有return 语句。
  • 添加:for await 不需要,你已经在等待上面的结果了。看看how to wrap setTimeout in a promise & return one
  • 但结果是反应也很快。我的意思是它不会像我每次迭代延迟 2 秒那样延迟。

标签: javascript node.js api express


【解决方案1】:

您应该从 sendSMS 返回承诺。在 setTimeout 回调函数中解析 promise。

function sendSMS(attendence){

    //console.log(target);
   return new Promise((resolve, reject) => {
   setTimeout(function(){
        let message = `Respected Parent, your son/daughter ${attendence.student.name} is absent on ${attendence.date}`;
        let messageURL = encodeURI(message); 
        let api = 'SOME VALUE';
        let phone = attendence.student.fphone.substring(1, 11); 
        let target = `http://BASE_URL/api.php?key=${api}&receiver=92${phone}&sender=DigitalPGS&msgdata=${messageURL}`; 

        resolve(1); 
    }, 2000); 
   });

}

【讨论】:

  • 另外让我知道,现在功能在几秒钟后响应,就像我设置了超时值一样。如果将来,我将请求发送到此 URL 以发送 SMS。 URL 需要一些时间才能响应,然后这个功能会起作用吗?
  • @YasirShahzad 您的setTimeout 在这里有点牵强。无论您打算使用什么来发送 SMS,如果它是一个承诺,就返回它。如果这不是一个承诺,那就合而为一。在这里使用 Promise 构造函数和 setTimeout 没有意义。
  • 如果异步函数需要超过 2 秒才能完成怎么办?这不起作用,这实际上不是在循环中处理异步函数的正确方法。您应该使用 async/await 和 for..of 来获得所需的结果。
【解决方案2】:

你应该让 sendSMS 返回一个承诺,然后等待:

exec();

async function exec()
{
  var records = [1,2,3];

  for(var i=0;i<records.length;i++)
  {
     var messageLogId = await sendSMS(records[i]);
     console.log("Result received from resolve", messageLogId);
  }
}

function sendSMS(record)
{
    // simulate an async method:
    return new Promise(function(resolve, reject) {
      setTimeout(function() {
        console.log("Send sms for record", record);
        resolve(1);
      }, 1000);
    });
}

注意这里的setTimeout 只是为了演示一个异步操作。在现实世界中,您的 sendSMS 函数无疑会调用一个 API,该 API 本身是异步的 - 只需从中返回承诺(或者,如果 API 客户端不返回,则将调用包装在承诺中)。

【讨论】:

    【解决方案3】:

    首先,将您的函数设为Promisify。然后分块你的函数并在for 循环中调用该函数并使用Promise.all() 处理。

    const manyPromises = [];
    
    for (const attendence of result.attendences) {
      manyPromises.push(sendSmsAndUpdateStatus(attendence));
    }
    // Execution wait until all promises fulfilled/rejected
    const result = await Promise.all(manyPromises);
    
    
    const sendSmsAndUpdateStatus = async (attendence) => {
      try {
        const messageLogId = await sendSMS(attendence);
        const updateObj = { smsLogId: messageLogId };
        const condition = { where: { id: attendence.id } };
        const result = await Attendence.update(updateObj, condition);
        return { result, messageLogId };
      } catch (err) {
        logger.error(err);
        throw err;
      }
    };
    
    const sendSMS = (attendence) => {
      return new Promise((resolve) => {
        setTimeout(() => {
          const message = `Respected Parent, your son/daughter ${attendence.student.name} is absent on ${attendence.date}`;
          const messageURL = encodeURI(message);
          const api = 'SOME VALUE';
          const phone = attendence.student.fphone.substring(1, 11);
          const target = `http://BASE_URL/api.php?key=${api}&receiver=92${phone}&sender=DigitalPGS&msgdata=${messageURL}`;
          return resolve(1);
        }, 2000);
      });
    };
    

    总结确保您的函数sendSMS 将返回Promise,之后您可以使用async/await.then().catch() 方法处理它。

    【讨论】:

    • await setTimeout( 什么都没做。
    • "后话" -> "afterwards"
    • @Jamiec 感谢您的建议,这是一个错字
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-11-29
    • 2018-02-08
    • 2015-12-22
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多