在地图中调用异步函数的最佳方式?

新手上路,请多包涵

我正在映射一个数组,对于新对象的返回值之一,我需要进行异步调用。

 var firebaseData = teachers.map(function(teacher) {
  return {
    name: teacher.title,
    description: teacher.body_html,
    image: urlToBase64(teacher.summary_html.match(/src="(.*?)"/)[1]),
    city: metafieldTeacherData[teacher.id].city,
    country: metafieldTeacherData[teacher.id].country,
    state: metafieldTeacherData[teacher.id].state,
    studioName: metafieldTeacherData[teacher.id].studioName,
    studioURL: metafieldTeacherData[teacher.id].studioURL
  }
});

该功能的实现看起来像

function urlToBase64(url) {
  request.get(url, function (error, response, body) {
    if (!error && response.statusCode == 200) {
      return "data:" + response.headers["content-type"] + ";base64," + new Buffer(body).toString('base64');
    }
  });
}

我不清楚这样做的最佳方法是什么……承诺?嵌套回调?在 ES6 或 ES7 中使用某些东西,然后使用 Babel 进行转译?

目前最好的实现方式是什么?

原文由 magician11 发布,翻译遵循 CC BY-SA 4.0 许可协议

阅读 247
2 个回答

一种方法是 Promise.all (ES6)

此答案适用于 Node 4.0+。旧版本将需要 Promise polyfill 或库。我还使用了 ES6 箭头函数,对于 Node < 4,您可以将其替换为常规的 function s。

此技术使用 Promise 手动包装 request.get 。您还可以使用像 request-promise 这样的库。

 function urlToBase64(url) {
  return new Promise((resolve, reject) => {
    request.get(url, function (error, response, body) {
      if (!error && response.statusCode == 200) {
        resolve("data:" + response.headers["content-type"] + ";base64," + new Buffer(body).toString('base64'));
      } else {
        reject(response);
      }
    });
  })
}

// Map input data to an Array of Promises
let promises = input.map(element => {
  return urlToBase64(element.image)
    .then(base64 => {
      element.base64Data = base64;
      return element;
    })
});

// Wait for all Promises to complete
Promise.all(promises)
  .then(results => {
    // Handle results
  })
  .catch(e => {
    console.error(e);
  })

原文由 joews 发布,翻译遵循 CC BY-SA 4.0 许可协议

2018 年更新: Promise.all 地图回调中的异步函数更容易实现:

     let firebaseData = await Promise.all(teachers.map(async teacher => {
        return {
            name: teacher.title,
            description: teacher.body_html,
            image: await urlToBase64(teacher.summary_html.match(/src="(.*?)"/)[1]),
            city: metafieldTeacherData[teacher.id].city,
            country: metafieldTeacherData[teacher.id].country,
            state: metafieldTeacherData[teacher.id].state,
            studioName: metafieldTeacherData[teacher.id].studioName,
            studioURL: metafieldTeacherData[teacher.id].studioURL
        }
    }));

async function urlToBase64(url) {
  return request.get(url, function (error, response, body) {
    if (!error && response.statusCode == 200) {
      return "data:" + response.headers["content-type"] + ";base64," + new Buffer(body).toString('base64');
    }
  });
}

Edit@2018/04/29 :我给大家放个通用的例子:

Edit@2019/06/19 : async/await 应该有 try/catch 来处理错误,让你的进程在某些请求失败的情况下继续工作。

 let data = await Promise.all(data.map(async (item) => {
      try {
      item.fetchItem = await fetchFunc(item.fetchParams);

      return item;
      } catch(error) {
         return {...item, error } ;
      }
  }));
  /* we can filter errors in data and retry later
   * eg:
   * const errorItems = data.filter(item => !!item.error)
   */


原文由 Kai 发布,翻译遵循 CC BY-SA 4.0 许可协议

撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进
推荐问题