跳至主要内容

[note] AsyncJS 筆記

keywords: async

AsyncJS @ github

API

map(coll, iteratee, callback[err, results])

  • mapSeries(coll, iteratee, callback<err, results>):用法和 .map() 的用法一樣,差別在於會在前一個 async function 執行結束後,才去執行下一個。
  • mapLimit(coll, limit, iteratee, callback<err, results>):用法和 .map() 的用法一樣,差別在於可以設定一次要非同步執行的數量。

這個函式會平行地執行透過 iteratee 帶入 coll 中的項目,而 iteratee 是非同步的方法,因此並不保證會照順序執行,但是最後的結果會照著 coll 的順序加以排列。

例子(map)

iteratee 這個 function 會包含兩個參數,第一個是從陣列 paths 取得的元素;第二個是 AsyncJS 中用的 callback

const async = require('async');
const fs = require('fs');

let paths = Array.from({ length: 10 }, (val, index) => index + 1); // [1, 2, 3, ..., 10]

/**
* 陣列 paths 中的每一個元素的會執行 getPathName 這個函式
* 並且在全部執行完後,執行 map 最後面的 callback function
**/
async.map(paths, getPathName, function (err, results) {
// results 會是所有 getPathName 中透過 callback 傳回來的內容
console.log(results);
});

/**
* getPathName 帶有兩個參數
* 第一個參數:陣列的元素
* 第二個參數: AsyncJS 中的 callback<err, result>,透過這個 callback 可以把結果丟回去
**/
function getPathName(path, callback) {
var delayTime = Math.floor(Math.random() * (1000 - 100 + 1)) + 100;
console.log(`${path} invoke function at delayTime(${delayTime})`);

setTimeout(function () {
console.log(path + ' finish function');
// 把執行後的結果丟回去主要的 map function
callback(null, `get path: ${path}`);
}, delayTime);
}

執行的結果會是:

# getPathName 會依照給入的元素順序執行
1 invoke function at delayTime(447)
2 invoke function at delayTime(152)
3 invoke function at delayTime(428)
4 invoke function at delayTime(414)
5 invoke function at delayTime(470)
6 invoke function at delayTime(861)
7 invoke function at delayTime(998)
8 invoke function at delayTime(978)
9 invoke function at delayTime(721)
10 invoke function at delayTime(314)

# 但因為是非同步函式,所以每個函式完成的時間順序不同
2 finish function
10 finish function
4 finish function
3 finish function
1 finish function
5 finish function
9 finish function
6 finish function
8 finish function
7 finish function

# results 最終的結果會按照陣列元素的順序來排列
[ 'get path: 1',
'get path: 2',
'get path: 3',
'get path: 4',
'get path: 5',
'get path: 6',
'get path: 7',
'get path: 8',
'get path: 9',
'get path: 10' ]

例子(mapSeries)

.mapSeries() 的用法和 .map() 的用法一樣,唯一的差別在於 .mapSeries() 會在前一個 async function 執行結束後,才去執行下一個,因此結果會像下面這樣:

# .mapSeries() 會把每一個 async function 執行完後才執行下一個:
1 invoke function at delayTime(110)
1 finish function
2 invoke function at delayTime(208)
2 finish function
3 invoke function at delayTime(978)
3 finish function
4 invoke function at delayTime(188)
4 finish function
5 invoke function at delayTime(523)
5 finish function
6 invoke function at delayTime(958)
6 finish function
7 invoke function at delayTime(537)
7 finish function
8 invoke function at delayTime(546)
8 finish function
9 invoke function at delayTime(671)
9 finish function
10 invoke function at delayTime(527)
10 finish function

# results
[ 'get path: 1',
'get path: 2',
'get path: 3',
'get path: 4',
'get path: 5',
'get path: 6',
'get path: 7',
'get path: 8',
'get path: 9',
'get path: 10' ]

each(coll, iteratee, callback[opt])

coll 中的每個元素以平行處理的方式套用到 iteratee<arg, cb> 這個 function 中,因為是平行處理的方式(in parallel),所以不保證執行的順序。

例子

const async = require('async');
let files = ['app.js', 'thisisnewspaper.js', 'map.js'];

// async.each(coll, iteratee<item, cb>, callback<err>)
async.each(files, processFile, (err) => {
if (err) {
console.log('A file failed to process');
} else {
console.log('All files have been processed successfully');
}
});

// iteratee<item, cb>
function processFile(file, callback) {
console.log('Processing file ' + file);

if (file.length > 10) {
console.log('This file name is too long');
callback('File name too long');
} else {
console.log('File processed');
callback();
}
}

執行結果如下:

----------------
Processing file app.js
File processed
----------------
Processing file thisisnewspaper.js
This file name is too long
A file failed to process
----------------
Processing file map.js
File processed

waterfall(tasks, callback[opt])

以順序的方式執行 tasks,並且把執行的結果,透過 callback(null, para) 帶到下一個函式中。

例子

const async = require('async');

async.waterfall([myFirstFunction, mySecondFunction, myLastFunction], function (err, result) {
if (err) {
throw new Error(err);
}
console.log('result', result); // done
});

function myFirstFunction(callback) {
callback(null, 'one', 'two');
}

function mySecondFunction(arg1, arg2, callback) {
console.log(arg1, arg2); // one, two
callback(null, 'three');
}

function myLastFunction(arg1, callback) {
console.log(arg1); // three
callback(null, 'done');
}

如果 waterfall 中的第一個函式想要代入變數,可以是用 async.constant(argument)

async.waterfall(
[
async.constant(42),
function (value, next) {
// value === 42
},
],
callback,
);

parallel(tasks, callback[opt])

當所有的 tasks 都完成後才會執行 callback 中的內容。

例子

/**
* tasks 以陣列的方式代入
* async.parallel([fn<cb>, fn<cb>], callback<err, results>)
**/
async.parallel(
[
function (callback) {
setTimeout(function () {
callback(null, 'one');
}, 2000);
},
function (callback) {
setTimeout(function () {
callback(null, 'two');
}, 100);
},
],

function (err, results) {
// 結果會是 ['one', 'two'] 即使第二個 function 的 timeout 時間較短
console.log(results);
},
);
/**
* tasks 以物件帶入,結果會根據 callback 的完成的時間排序
* async.parallel({one: fn<cb>, two: fn<cb>}, callback<err, results>)
**/
async.parallel({
one: function(callback) {
setTimeout(function() {
callback(null, 1);
}, 200);
},
two: function(callback) {
setTimeout(function() {
callback(null, 2);
}, 100);
}
}, function(err, results) {
// results is now equals to:
console.log(results) // { two: 2, one: 1 }
});