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

mag*_*n11 27 javascript node.js

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

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
  }
});
Run Code Online (Sandbox Code Playgroud)

该函数的实现看起来像

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');
    }
  });
}
Run Code Online (Sandbox Code Playgroud)

我不清楚做到这一点的最佳方法是什么......承诺?嵌套回调?在ES6或ES7中使用一些东西然后用Babel进行转换?

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

谢谢!

joe*_*ews 35

一种方法是Promise.all(ES6).

这个答案适用于Node 4.0+.旧版本需要Promise polyfill或库.我还使用了ES6箭头函数,你可以用常规的functions代替Node <4.

这种技术手动包装request.getPromise.您还可以使用像请求承诺这样的库.

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);
  })
Run Code Online (Sandbox Code Playgroud)

  • 如果你问我,那就是非常通用的代码 用他给出的代码看起来怎么样? (3认同)

Kai*_*Kai 29

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');
    }
  });
}
Run Code Online (Sandbox Code Playgroud)

编辑@ 2018/04/29:我为大家举了一般例子:

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

      return item; 
      } catch(err) {
         throw err;
      }
  });
Run Code Online (Sandbox Code Playgroud)

  • `.map()` 函数将返回一个 `Promise` 数组,而 `Promise.all()` 将解决该并行问题。该语法可能会让您与 `return item` 混淆,它看起来只是返回一个变量而不是承诺:但该语法等于 `function() { item = data[i]; 返回新的 Promise(resolve =&gt; fetchFunc().then(result =&gt; {item.fetchItem = result; resolve(item); } )}` (2认同)

Eze*_*lla 7

您可以使用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);
}
Run Code Online (Sandbox Code Playgroud)

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

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

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

  • 它有一个 lodash 依赖...并不是说这有什么问题。异步@2.6.1 ~ lodash@4.17.11 (3认同)

Lia*_*iam 6

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){
   ....
}
Run Code Online (Sandbox Code Playgroud)

  • ECMAscript 标准机构现在已经带我们回到了原点。在 JS 的黑暗早期,我们对所有事情都坚持使用 `for` 循环(或者我们必须使用 jQuery、Lodash 等来获取 `map`)。然后是 ES2015,各地的开发者都为终于能够使用地图而欢欣鼓舞。但现在我们又回到了“for”循环的困境,至少对于“await”代码来说……前进两步,后退一步:( (7认同)

小智 5

我遇到了类似的问题,发现这更容易(我正在使用 Kai 的通用模板)。下面,您只需要使用一个await. 我还使用 ajax 函数作为我的异步函数:

function asyncFunction(item) {
    return $.ajax({
        type: "GET",
        url: url,
        success: response => {
            console.log("response received:", response);
            return response;
        }, error: err => {
            console.log("error in ajax", err);
        }
    });
}

let data = await Promise.all(data.map(item => asyncFunction(item)));
Run Code Online (Sandbox Code Playgroud)