2015-10-30 43 views
10

我正在映射一個數組和一個新對象的返回值,我需要做一個異步調用。在地圖中調用異步函數的最佳方法是什麼?

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進行傳輸?

當前最好的實現方法是什麼?

謝謝!

+1

也許塔卡看看https://github.com/caolan /異步映射功能 – Herku

+0

您必須在內部映射中使用的函數中實現回調或承諾。 – Danmoreng

回答

20

一種方法是Promise.all (ES6)

此答案將在Node 4.0+中工作。較早的版本需要一個Promise polyfill或庫。我還使用了ES6箭頭功能,您可以用常規function替換節點< 4.

此技術用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); 
    }) 
+2

如果你問我,可以使用非常通用的代碼。他的代碼如何看起來像? – Danmoreng

+1

另外我想他希望在服務器端使用Node.js,因爲他要求我承諾我期望他使用最新版本。我對自己感興趣,我目前使用回調映射到具有異步請求的數組上,存儲請求數+請求數,並且只有在完成所有請求時才執行終止函數(這是所有請求的回調函數)。 Promises.all看起來像一個更優雅的方式來做到這一點。 – Danmoreng

+1

Thanks @joews我將NodeJS更新爲v5,此代碼運行良好。 – magician11

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

注意,所有教師將並行處理 - 你也可以使用這個功能:通過一個

mapLimit(arr, limit, iterator, [callback])

mapSeries(arr, iterator, [callback])映射一個在同一時間映射limit

0

我使用數組上的異步函數。而不是使用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 []; 
    } 
}; 
1

2017年 更多新的圖書館讓我的單頁的應用程序有超過4個MEGAS建。所以我決定不要添加更多像lodash這樣的新庫,可以用代碼替代。使用我很好的解決方案地圖等待是:

​​
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 
     } 
    })); 
相關問題