Node.js Async 流程控制庫與 Promise 流程控制

Async庫學習筆記

Async是個功能比較強大的異步流程控制庫,常用的流程控制模式有串行,並行和瀑布流模式。注意和【async await】區分開。

1. Async串行控制

串行控制的函數爲

function series(tasks, callback) {..}

參數tasks爲任務函數的數組,callback爲回調函數。且數組中每個函數都有一個包含兩個參數[error & result]的回調函數。數組中所有的函數成功串行執行完畢或者出錯都直接執行series的回調函數。

示例:
有三個函數串行執行,每個函數運行時間爲1s,且出錯概率爲20%。出錯直接拋出一個錯誤並進入最後的回調函數,不再執行後續的函數。

const async = require('async');

async.series([
    function (callback) {
        setTimeout(function () {
            if (Math.random() < 0.2) {
                callback(new Error(), 'f1 error');
            } else {
                console.log('1 over');
                callback(null, new Date().toLocaleString());
            }
        }, 1000);
    },
    function (callback) {
        setTimeout(function () {
            if (Math.random() < 0.2) {
                callback(new Error(), 'f2 error');
            } else {
                console.log('2 over');
                callback(null, new Date().toLocaleString());
            }
        }, 1000);
    },
    function (callback) {
        setTimeout(function () {
            if (Math.random() < 0.2) {
                callback(new Error(), 'f3 error');
            } else {
                console.log('3 over');
                callback(null, new Date().toLocaleString());
            }
        }, 1000);
    }
], function (error, results) {
    if (error) {
        console.log('error: ');
        console.log(results);
    } else {
        console.log('time: ', results);
    }
});

輸出可能的結果爲
Output1 三個函數都沒出錯·:

1 over
2 over
3 over
time:  [ '2019-11-9 16:54:02', '2019-11-9 16:54:03', '2019-11-9 16:54:04' ]

Output2 第二個函數出錯了:

1 over
error: 
[ '2019-11-9 16:54:18', 'f2 error' ]

1.1 Promise實現的串行控制

下面用鏈式的Promise來實現上述串行控制,唯一不同的一點是async.series函數回調函數可以獲取所有程序的返回結果,鏈式Promise最後只能獲取到一個resolve的結果,不過可以通過別的方法實現。

new Promise(function (resolve, reject) {
    setTimeout(function () {
        if (Math.random() < 0.2) {
            reject('f1 error');
        } else {
            console.log('1 over');
            resolve(new Date().toLocaleString());
        }
    }, 1000);
}).then(function () {
    return new Promise(function(resolve, reject){
        setTimeout(function () {
            if (Math.random() < 0.2) {
                reject('f2 error');
            } else {
                console.log('2 over');
                resolve(new Date().toLocaleString());
            }
        }, 1000);
    })
}).then(function () {
    return new Promise(function(resolve, reject){
        setTimeout(function () {
            if (Math.random() < 0.2) {
                reject('f3 error');
            } else {
                console.log('3 over');
                resolve(new Date().toLocaleString());
            }
        }, 1000);
    })
}).then(function (results) {
    console.log('time: ', results);
}).catch(function (error) {
    console.log('error: ');
    console.log(error);
});

2. Async瀑布模式控制

上面介紹是串行控制,且各個函數之間沒有數據交互。如果前面一個函數作爲後一個函數的輸入參數的話就要用到瀑布流控制函數。瀑布流函數爲:

function waterfall(tasks, callback) {..}

示例:
和上面例子相似,有三個數串行執行,每個函數運行時間爲1s,且出錯概率爲20%。前一個函數的回調函數中的數據傳給下一個函數作爲參數。

const async = require('async');

async.waterfall([
    function (callback) {
        console.log("I am f1");
        if (Math.random() < 0.2) {
            callback(new Error(), 'f1 error');
        } else {
            callback(null, 'res-1');
        }
    },
    function (data, callback) {
        console.log("I am f2");
        console.log('get data', data);
        if (Math.random() < 0.2) {
            callback(new Error(), 'f2 error');
        } else {
            callback(null, 'res-2');
        }
    },
    function (data, callback) {
        console.log("I am f3");
        console.log('get data', data);
        if (Math.random() < 0.2) {
            callback(new Error(), 'f3 error');
        } else {
            callback(null, 'res-3');
        }
    }], function (error, result) {
        if (error) {
            console.log('error: ', result);
        } else {
            console.log('result: ', result);
        }
    });

和上面例子一樣是串行執行的,所以就不加延時函數了。函數數組除了第一個函數只有一個參數,後面的函數都有兩個參數,且第一個參數爲上一個函數的callback中的第二個數據。
輸出
Output1 無錯誤時:

I am f1
I am f2
get data res-1
I am f3
get data res-2
result:  res-3

可以看出函數2的參數data即爲函數1的回調返回值。

Output 2 函數2出錯時:

I am f1
I am f2
get data res-1
error:  f2 error

2.1 Promise 實現的瀑布模式控制

這個和上一個類似,貌似很easy。只需要在後面的then函數裏面加個參數就能收到上一個函數的結果了。同樣是出錯直接進入catch。

new Promise(function (resolve, reject) {
    console.log('I am f1');
    if (Math.random() < 0.2) {
        reject('f1 error');
    } else {
        resolve('res-1');
    }
}).then(function (data) {
    return new Promise(function (resolve, reject) {
        console.log("I am f2");
        console.log('get data', data);
        if (Math.random() < 0.2) {
            reject('f2 error');
        } else {
            resolve('res-2');
        }
    })
}).then(function (data) {
    return new Promise(function(resolve, reject){
        console.log("I am f3");
        console.log('get data', data);
        if (Math.random() < 0.2) {
            reject('f3 error');
        } else {
            resolve('res-3');
        }
    })
}).then(function (results) {
    console.log('time: ', results);
}).catch(function (error) {
    console.log('error: ');
    console.log(error);
});

Async 並行控制

這個並行控制的函數和js資審異步機制差不多,幾個函數都是同時運行的。有點類似Promise.all,幾個函數同時運行,沒有錯誤執行相應的操作,有錯誤則報錯。直接看例子。
示例:

const async = require('async');
async.parallel([
    function (callback) {
        setTimeout(function () {
            callback(null, 'first ' + new Date().toLocaleString());
        }, 2000);
    },
    function (callback) {
        setTimeout(function () {
            callback(null, 'second ' + new Date().toLocaleString());
        }, 1000);
    }],
    function (error, results) {
        console.log(error);
        console.log(results);
    });

輸出:

null
[ 'first 2019-11-9 21:16:08', 'second 2019-11-9 21:16:07' ]

可以看出第二個函數先調用的回調函數,這個和Promise.all差不多。

3.1 Promise 實現並行控制

前面也說了和Promise.all類似,就寫個類似的栗子吧。

let fun1 = new Promise(function (resolve, reject) {
    setTimeout(function () {
        if (Math.random() < 0.2) {
            console.log('I am f1');
            reject('f1 error');
        } else {
            resolve(new Date().toLocaleString());
        }
    }, 2000)
});

let fun2 = new Promise(function (resolve, reject) {
    setTimeout(function () {
        if (Math.random() < 0.2) {
            console.log("I am f2");
            reject('f2 error');
        } else {
            resolve(new Date().toLocaleString());
        }
    }, 1000);
});

let fun3 = new Promise(function (resolve, reject) {
    setTimeout(function () {
        if (Math.random() < 0.2) {
            console.log("I am f3");
            reject('f3 error');
        } else {
            resolve(new Date().toLocaleString());
        }
    }, 1000);
});

Promise.all([fun1, fun2, fun3]).then(function (results) {
    console.log('success: ', results);  //返回三個函數的resolve內容
}).catch(function (error) {
    console.log('error:', error);
});

總結

研究了小半天突然發現這個Async並沒啥卵用啊,起碼這3個簡單的函數的功能,Promise都可以實現。唯一好處就是看着更簡潔一些。不過話說回來,簡潔也是一大優點。。Promise鏈式調用與層層嵌套的回調相比也就是看着簡潔。剛入門Node.js,記錄一下作學習筆記。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章