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

IT技术 javascript node.js asynchronous
2021-02-10 20:10:26

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

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');
    }
  });
}

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

目前实现这一目标的最佳方法是什么?

6个回答

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(err) {
         throw err;
      }
  }));
.map()函数将返回一个 数组Promise,并将Promise.all()解析该并行。该语法可能会使您与 混淆return item,它看起来只是返回一个变量而不是Promise:但该语法等于function() { item = data[i]; return new Promise(resolve => fetchFunc().then(result => {item.fetchItem = result; resolve(item); } )}
2021-03-31 20:10:26
保持地图回调函数异步有什么意义,如果 fetchFunc 正在返回Promise,那么 Promise.all 应该负责解决所有的Promise。我觉得只有当您想按顺序解决所有Promise时才需要这样做。
2021-04-10 20:10:26

一种方法是Promise.all(ES6)

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

这种技术request.get用 Promise手动包装您还可以使用像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);
  })
如果你问我,非常通用的代码。他提供的代码看起来如何?
2021-03-18 20:10:26
谢谢 @joews 我将 NodeJS 更新到 v5,这段代码运行良好。
2021-04-01 20:10:26
另外,我认为他想在 Node.js 的服务器端使用它,因为他要求Promise我希望他使用最新版本。我自己对此很感兴趣,我目前使用回调来映射具有异步请求的数组,存储请求的数量 + 完成的请求并仅在所有请求都完成时才执行完成函数(这是所有请求的回调)。Promises.all 看起来更优雅。
2021-04-05 20:10:26
这是唯一对我在地图内的异步函数有用的语法
2021-04-11 20:10:26

您可以使用async.map

var async = require('async');

async.map(teachers, mapTeacher, function(err, results){
  // results is now an array of stats for each file
});

function mapTeacher(teacher, done) {
  // computing stuff here...
  done(null, teacher);
}

请注意,所有教师都将并行处理 - 您也可以使用以下功能:

mapSeries(arr, iterator, [callback]) 一张一张的地图

mapLimit(arr, limit, iterator, [callback])地图limit在同一时间

能够限制同时异步函数的数量至关重要
2021-04-04 20:10:26
它有一个 lodash 依赖...并不是说这有什么问题。异步@2.6.1 ~ lodash@4.17.11
2021-04-06 20:10:26

在 2020 年,我们现在拥有ECMAScript2021for await...of语法可以显着简化事情:

所以你现在可以简单地这样做:

//return an array of promises from our iteration:
let promises = teachers.map(async m => {
   return await request.get(....);
});

//simply iterate those
//val will be the result of the promise not the promise itself
for await (let val of promises){
   ....
}
ECMAscript 标准机构现在已经把我们带回了一个完整的圈子。在 JS 的黑暗早期,我们被for所有东西都困在循环中(或者我们不得不使用 jQuery、Lodash 等来获取map)。然后是 ES2015,各地的开发者都为终于可以使用 map 感到高兴。但是现在我们又回到了for循环中,至少对于await代码来说......向前两步,向后一步:(
2021-03-23 20:10:26

我在数组上使用异步函数。而不是使用array.map,而是一个for函数。它是这样的:

const resultingProcessedArray = async function getSomeArray() {
    try {
      let { data } = await axios({url: '/myUrl', method:'GET'}); //initial array
      let resultingProcessedArray = [];
      for (let i = 0, len = data.items.length; i < len; i++) {
        let results = await axios({url: `/users?filter=id eq ${data.items[i].someId}`, method:'GET'});
        let domainName = results.data.items[0].domainName;
        resultingProcessedArray.push(Object.assign(data.items[i], {domainName}));
      }
      return resultingProcessedArray;
    } catch (err) {
      console.error("Unable to fetch the data", err);
      return [];
    }
};