使用 Array.map 时嵌套承诺,使用 Promise.all 但仍然无法正常工作

Posted

技术标签:

【中文标题】使用 Array.map 时嵌套承诺,使用 Promise.all 但仍然无法正常工作【英文标题】:Nested promises while using Array.map, using Promise.all but still not working 【发布时间】:2017-08-28 04:10:29 【问题描述】:

我的头撞墙已经有一段时间了。我有一些节点代码可以提取一堆 googlecalendar 事件,然后将它们映射到不同的对象结构上以在我的 webapp 中使用...作为其中的一部分,我需要将 google 日历事件中的用户与我的 webapp 中的用户匹配数据库。代码如下:

router.post('/getCalendar', function(req,res)   
  // authentication
  var oauth2Client = getOAuthClient();
  var session = req.session;
  oauth2Client.setCredentials(
    access_token: req.user.google.token,
    refresh_token: req.user.google.refreshToken,
    expiry_date: true
  )

  var p = new Promise(function (resolve, reject) 
    // get the next 20 calendar events   
    var calendar = google.calendar('v3');
      calendar.events.list(
        auth: oauth2Client,
        calendarId: 'primary',
        timeMin: (new Date()).toISOString(),
        maxResults: 20,
        singleEvents: true,
        orderBy: 'startTime'
      , function(err, response) 
        if (err) console.log('err',err)
          // displays all the events
        if (response) console.log('response',response)
        if(response) 
          var events = response.items;
          resolve(events || err);  
                
    );

  ).then(function (data) 

    var newdata = data.map(function(item) 
      // create the new object structure and populate with some details
      newObj = 
          _id: item.id,
          _title: item.summary,
          group: [req.headers.group],
          fields: [],
          assignedTo: [],
          google: "calendar",
          googledata: item,
          _category: []
      

      var extendedFields = 

      // get the list of attendees
      attendees = item.attendees.map(function(user) 
        var attendee = user.email;
        return attendee;
      )

      // pulls the user information for each attendee using promises
      var promises = attendees.map(function(email) 
        return new Promise(function(resolve, reject) 

          // data is in a mongodb, using mongoose
          User.findOne('google.email': email).exec(function (err, user) 
            if (err) return resolve(null);
            resolve(user); //not using reject at all so the Promise.all shouldn't fail
          );
        )
      )

      Promise.all(promises)
        .then(function(value) 
          value.forEach(function(user) 
            if(promise !== null) 
              newObj.assignedTo.push(user);  
            
          );

          return newObj

      ).catch((error) => console.log(error))

  )

  res.json(newdata)
  )

);

我正在使用 Promise.all 等到所有承诺都已解决,但它似乎在完成第一项之前移动到 data 数组中的下一项。欢迎任何指导。如果需要更多详细信息,请告诉我。

谢谢。

【问题讨论】:

没有回复时别忘了reject data 数组也需要使用 Promise.allmap @Bergi - 我认为当我使用reject 时,Promise.all 会失败...我也不确定如何将 Promise.all 用于mapdata正如你所建议的。您能否提供更多解释或示例? 我的意思是 calendar.events.list 调用 - 如果你不履行承诺(履行或拒绝),它将永远挂起。 你已经为 attendees 做的很好 - 现在只需为 data 数组做同样的事情。 return 来自map 回调的“内部”Promise.all(…) 承诺,因此newdata 又是一组承诺 【参考方案1】:

    返回最后一个 Promise.all 的承诺。

    return Promise.all(promises)
        .then(function(value) 
          value.forEach(function(user) 
            // user !== null ? 
            if(promise !== null) 
              newObj.assignedTo.push(user);  
            
          );
    
          return newObj
        )
        .catch((error) => console.log(error))
    

    在新数据上使用 Promise.all。

    Promise.all(newdata)
      .then(function (newObjs) 
        // newObjs is the Array of newObj in your code
        res.json(newObjs);
      )
      .catch(function (err) 
        console.log(err);
      );
    

【讨论】:

以上是关于使用 Array.map 时嵌套承诺,使用 Promise.all 但仍然无法正常工作的主要内容,如果未能解决你的问题,请参考以下文章

向返回承诺数组的 Array.map 调用添加毫秒延迟

干净的代码和嵌套的承诺 [重复]

蓝鸟嵌套承诺与每个/传播

嵌套承诺和拒绝

在开始下一个之前等待上一个承诺的Javascript Map?

蓝鸟承诺 - 嵌套与拒绝模式