Лучший способ вызвать асинхронную функцию в карте?

22

Я сопоставляю массив и для одного из возвращаемых значений нового объекта, мне нужно сделать асинхронный вызов.

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

Я не знаю, какой лучший подход для этого... promises? Вложенные обратные вызовы? Использовать что-то в ES6 или ES7, а затем перетащить с помощью Babel?

Каков наилучший способ реализации этого?

Спасибо!

  • 1
    Может быть, взглянуть на функцию карты github.com/caolan/async
  • 0
    Вы должны были бы реализовать или обратные вызовы или обещания в функции, которую вы используете внутри карты.
Теги:

5 ответов

30
Лучший ответ

Одним из подходов является Promise.all (ES6).

Этот ответ будет работать в Узле 4. 0+. В старых версиях потребуется полифилл или библиотека Promise. Я также использовал функции стрелок ES6, которые можно заменить обычной function для узла <4.

Этот метод вручную оборачивает request.get Обещанием. Вы также можете использовать библиотеку, как запрос-обещание.

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);
  })
  • 3
    Довольно общий код, если вы спросите меня. Как это будет выглядеть с кодом, который он дал?
  • 1
    Кроме того, я думаю, что он хочет использовать его на стороне сервера с Node.js, так как он попросил обещания, я ожидаю, что он будет использовать последнюю версию. Я заинтересован в этом сам, в настоящее время я использую обратные вызовы для сопоставления массива с асинхронными запросами, сохраняю количество запросов + выполненных запросов и выполняю функцию завершения (которая является обратным вызовом для всех запросов), только если все запросы выполнены. Promises.all выглядит как более элегантный способ сделать это.
Показать ещё 1 комментарий
22

Обновление в 2018: Promise.all функцию 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: я поставил общий пример для всех:

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

      return item;
  });
  • 0
    Какой смысл сохранять функцию обратного вызова карты асинхронной, если fetchFunc возвращает обещание, тогда Promise.all должен позаботиться о разрешении всех обещаний. Я чувствую, что это требуется только тогда, когда вы хотите выполнить все обещания последовательно.
  • 0
    Функция .map() вернет массив Promise , и Promise.all() разрешит эту параллель. Этот синтаксис может сбить вас с толку return item , похоже, он просто возвращает переменную, а не обещание: но этот синтаксис равен function() { item = data[i]; return new Promise(resolve => fetchFunc().then(result => {item.fetchItem = result; resolve(item); } )}
4

Вы можете использовать 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 в то же время

  • 0
    у него зависимость от lodash ... не то, чтобы с этим было что-то не так. [email protected]~ [email protected]
2

2019

предупреждение с Promise.all, они не выполняются в точном порядке, некоторые серверы не могут поддерживать одновременное выполнение запросов или API-вызовов.

Более новые библиотеки делают мои одностраничные приложения более 4 мегапикселями. Поэтому я решил не добавлять больше новых библиотек, таких как lodash и т.д., Которые можно заменить кодом. Я хорошие решения для использования ждут с картой:

 rows.map (  ( record ) => {
    try {
      (async () => {
        let col =  await client.query('SELECT * FROM customers');
      })(); 
    } catch (err) {
      console.log(err);
    }
 });
1

Я использую асинхронную функцию над массивом. И не использовать array.map, а для функции. Это что-то вроде этого:

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 [];
    }
};

Ещё вопросы

Сообщество Overcoder
Наверх
Меню