1. 程式人生 > >Async 詳解

Async 詳解

為了適應非同步程式設計,減少回撥的巢狀,我嘗試了很多庫。最終覺得還是async最靠譜。

Async的內容分為三部分:

  1. 流程控制:簡化十種常見流程的處理
  2. 集合處理:如何使用非同步操作處理集合中的資料
  3. 工具類:幾個常用的工具類

本文介紹其中最簡單最常用的流程控制部分。

由於nodejs是非同步程式設計模型,有一些在同步程式設計中很容易做到的事情,現在卻變得很麻煩。Async的流程控制就是為了簡化這些操作。

1. series(tasks, [callback]) (多個函式依次執行,之間沒有資料交換)

有多個非同步函式需要依次呼叫,一個完成之後才能執行下一個。各函式之間沒有資料的交換,僅僅需要保證其執行順序。這時可使用series。

純js程式碼:

step1(function(err, v1){
  step2(function(err, v2){
    step3(function(err, v3){// do somethig with the err or values v1/v2/v3}}});

從中可以看到這巢狀還是比較多深的,如果再多幾步,會更深。在程式碼中忽略對了每一層err的處理,否則還都等加上 if(err) return callback(err),那就更麻煩了。

對於這種情況,使用async來處理,就是這樣的:

var async =require('async')
async.series([
   step1
, step2, step3 ],function(err, values){// do somethig with the err or values v1/v2/v3});

可以看到程式碼簡潔了很多,而且自動處理每個回撥中的錯誤。當然,這裡只給出來最最簡單的例子,在實際中,我們常會在每個step中執行一些操作,這時可寫成:

var async =require('async')
async.series([function(cb){ step1(function(err,v1){// do something with v1
     cb(err, v1);}),function(cb){ step2
(...)},function(cb){ step3(...)}],function(err, values){// do somethig with the err or values v1/v2/v3});

該函式的詳細解釋為:

  1. 依次執行一個函式陣列中的每個函式,每一個函式執行完成之後才能執行下一個函式。
  2. 如果任何一個函式向它的回撥函式中傳了一個error,則後面的函式都不會被執行,並且將會立刻會將該error以及已經執行了的函式的結果,傳給series中最後那個callback。
  3. 當所有的函式執行完後(沒有出錯),則會把每個函式傳給其回撥函式的結果合併為一個數組,傳給series最後的那個callback。
  4. 還可以json的形式來提供tasks。每一個屬性都會被當作函式來執行,並且結果也會以json形式傳給series最後的那個callback。這種方式可讀性更高一些。

其程式碼中還包含了:

  1. 如果中間某個函數出錯,series函式如何處理
  2. 如果某個函式傳給回撥的值為undefined, null, {}, []等,series如何處理

另外還需要注意的是:多個series呼叫之間是不分先後的,因為series本身也是非同步呼叫。

2. parallel(tasks, [callback]) (多個函式並行執行)

並行執行多個函式,每個函式都是立即執行,不需要等待其它函式先執行。傳給最終callback的陣列中的資料按照tasks中宣告的順序,而不是執行完成的順序。

如果某個函數出錯,則立刻將err和已經執行完的函式的結果值傳給parallel最終的callback。其它未執行完的函式的值不會傳到最終資料,但要佔個位置。

同時支援json形式的tasks,其最終callback的結果也為json形式。

示例程式碼:

async.parallel([function(cb){ t.fire('a400', cb,400)},function(cb){ t.fire('a200', cb,200)},function(cb){ t.fire('a300', cb,300)}],function(err, results){
    log('1.1 err: ', err);// -> undefined
    log('1.1 results: ', results);// ->[ 'a400', 'a200', 'a300' ]});

中途出錯的示例:

async.parallel([function(cb){ log('1.2.1: ','start'); t.fire('a400', cb,400)},// 該函式的值不會傳給最終callback,但要佔個位置function(cb){ log('1.2.2: ','start'); t.err('e200', cb,200)},function(cb){ log('1.2.3: ','start'); t.fire('a100', cb,100)}],function(err, results){
    log('1.2 err: ', err);// -> e200
    log('1.2 results: ', results);// -> [ , undefined, 'a100' ]});

以json形式傳入tasks

async.parallel({
    a:function(cb){ t.fire('a400', cb,400)},
    b:function(cb){ t.fire('c300', cb,300)}},function(err, results){
    log('1.3 err: ', err);// -> undefined
    log('1.3 results: ', results);// -> { b: 'c300', a: 'a400' }});

3. waterfall(tasks, [callback]) (多個函式依次執行,且前一個的輸出為後一個的輸入)

與seires相似,按順序依次執行多個函式。不同之處,每一個函式產生的值,都將傳給下一個函式。如果中途出錯,後面的函式將不會被執行。錯誤資訊以及之前產生的結果,將傳給waterfall最終的callback。

這個函式名為waterfall(瀑布),可以想像瀑布從上到下,中途衝過一層層突起的石頭。注意,該函式不支援json格式的tasks。

async.waterfall([function(cb){ log('1.1.1: ','start'); cb(null,3);},function(n, cb){ log('1.1.2: ',n); t.inc(n, cb);},function(n, cb){ log('1.1.3: ',n); t.fire(n*n, cb);}],function(err, result){
    log('1.1 err: ', err);// -> null
    log('1.1 result: ', result);// -> 16});

4. auto(tasks, [callback]) (多個函式有依賴關係,有的並行執行,有的依次執行)

用來處理有依賴關係的多個任務的執行。比如某些任務之間彼此獨立,可以並行執行;但某些任務依賴於其它某些任務,只能等那些任務完成後才能執行。

雖然我們可以使用async.parallel和async.series結合起來實現該功能,但如果任務之間關係複雜,則程式碼會相當複雜,以後如果想新增一個新任務,也會很麻煩。這時使用async.auto,則會事半功倍。

如果有任務中途出錯,則會把該錯誤傳給最終callback,所有任務(包括已經執行完的)產生的資料將被忽略。

這裡假設我要寫一個程式,它要完成以下幾件事:

  1. 從某處取得資料
  2. 在硬碟上建立一個新的目錄
  3. 將資料寫入到目錄下某檔案
  4. 傳送郵件,將檔案以附件形式傳送給其它人。

分析該任務,可以知道1與2可以並行執行,3需要等1和2完成,4要等3完成。

async.auto({
    getData:function(callback){
        setTimeout(function(){
            console.log('1.1: got data');
            callback();},300);},
    makeFolder:function(callback){
        setTimeout(function(){
            console.log('1.1: made folder');
            callback();},200);},
    writeFile:['getData','makeFolder',function(callback){
        setTimeout(function(){
            console.log('1.1: wrote file');
            callback(null,'myfile');},300);}],
    emailFiles:['writeFile',function(callback, results){
        log('1.1: emailed file: ', results.writeFile);// -> myfile
        callback(null, results.writeFile);}]},function(err, results){
    log('1.1: err: ', err);// -> null
    log('1.1: results: ', results);// -> { makeFolder: undefined,//      getData: undefined,//      writeFile: 'myfile',//      emailFiles: 'myfile' }});

5. whilst(test, fn, callback)(用可於非同步呼叫的while)

相當於while,但其中的非同步呼叫將在完成後才會進行下一次迴圈。舉例如下:

var count1 =0;
async.whilst(function(){return count1 <3},function(cb){
        log('1.1 count: ', count1);
        count1++;
        setTimeout(cb,1000);},function(err){// 3s have passed
        log('1.1 err: ', err);// -> undefined});

它相當於:

try{
  whilst(test){
    fn();}
  callback();}catch(err){
  callback(err);}

該函式的功能比較簡單,條件變數通常定義在外面,可供每個函式訪問。在迴圈中,非同步呼叫時產生的值實際上被丟棄了,因為最後那個callback只能傳入錯誤資訊。

另外,第二個函式fn需要能接受一個函式cb,這個cb最終必須被執行,用於表示出錯或正常結束。

6. until(test, fn, callback) (與while相似,但判斷條件相反)

var count4 =0;
async.until(function(){return count4>3},function(cb){
        log('1.4 count: ', count4);
        count4++;
        setTimeout(cb,200);},function(err){// 4s have passed
        log('1.4 err: ',err);// -> undefined});

當第一個函式條件為false時,繼續執行第二個函式,否則跳出。

7. queue (可設定worker數量的佇列)

queue相當於一個加強版的parallel,主要是限制了worker數量,不再一次性全部執行。當worker數量不夠用時,新加入的任務將會排隊等候,直到有新的worker可用。

該函式有多個點可供回撥,如worker用完時、無等候任務時、全部執行完時等。

定義一個queue,其worker數量為2,並在任務執行時,記錄一下日誌:

var q = async.queue(function(task, callback){
    log('worker is processing task: ', task.name);
    task.run(callback);},2);

worker數量將用完時,會呼叫saturated函式:

q.saturated =function(){
    log('all workers to be used');}

當最後一個任務交給worker執行時,會呼叫empty函式

q.empty =function(){
    log('no more tasks wating');}

當所有任務都執行完時,會呼叫drain函式

q.drain =function(){
    console.log('all tasks have been processed');}

放入多個任務,可一次放一個,或一次放多個

q.push({name:'t1', run:function(cb){
    log('t1 is running, waiting tasks: ', q.length());
    t.fire('t1', cb,400);// 400ms後執行}},function(err){
    log('t1 executed');});
q.push
            
           

相關推薦

koa2第一天 async

一、什麼是async    async其實是ES7的才有的關鍵字,放在這裡說,其實是和我們前面所說的Promise,Generator有很大關聯的。async的意思是"非同步",顧名思義是有關非同步操作有關的關鍵字。下面我們就來構造一個async方法。 async function

nodejs Async

為了適應非同步程式設計,減少回撥的巢狀,我嘗試了很多庫。最終覺得還是async最靠譜。 Async的內容分為三部分: 流程控制:簡化十種常見流程的處理集合處理:如何使用非同步操作處理集合中的資料工具類:幾個常用的工具類 本文介紹其中最簡單最常用的流程控制部分。 由於node

用於非同步載入的defer和async

定義和用法 <script type="text/javascript" defer="defer"> alert(document.getElementById("p1").first

Async之一:流程控制

為了適應非同步程式設計,減少回撥的巢狀,我嘗試了很多庫。最終覺得還是async最靠譜。 Async的內容分為三部分: 流程控制:簡化十種常見流程的處理 集合處理:如何使用非同步操作處理集合中的資料 工具類:幾個常用的工具類 本文介紹其中最簡單最常用的流程控制部分。

Async

為了適應非同步程式設計,減少回撥的巢狀,我嘗試了很多庫。最終覺得還是async最靠譜。 Async的內容分為三部分: 流程控制:簡化十種常見流程的處理集合處理:如何使用非同步操作處理集合中的資料工具類:幾個常用的工具類本文介紹其中最簡單最常用的流程控制部分。 由於nodejs是非同步程式設計模型,有一些在

ES6中的promise、async、await用法

res color spa tle turn 同步方法 set 調用 順序輸出 <!DOCTYPE html> <html> <head> <title>Promise、async、await</title&g

async/await使用深入

async和await作為非同步模型程式碼編寫的語法糖已經提供了一段時間不過一直沒怎麼用,由於最近需要在BeetleX webapi中整合對Task方法的支援,所以對async和await有了深入的瞭解和實踐應用.在這總結一下async和await的使用,主要涉及到:自定義Awaitable,在傳統非同步方法

現代JS中的流程控制:Callbacks 、Promises 、Async/Await

JavaScript經常聲稱是_非同步_。那是什麼意思?它如何影響發展?近年來這種方法有何變化? 請思考以下程式碼: result1 = doSomething1(); result2 = doSomething2(result1); 大多數語言都處理每一行同步。第一行執行並返回結果。第二行在第一行完

Spring中@Async用法及簡單例項

Spring中@Async用法 引言: 在Java應用中,絕大多數情況下都是通過同步的方式來實現互動處理的;但是在處理與第三方系統互動的時候,容易造成響應遲緩的情況,之前大部分都是使用多執行緒來完成此類任務,其實,在spring 3.x之後,就已經內建了@Async來完美解決這個問題,本文將完成

「微信小程式」PHP非同步程序async-helper例項

PHP非同步程序async-helper例項詳解 PHP 的非同步程序助手,藉助於 AMQP 實現非同步執行 PHP 的方法,將一些很耗時、追求高可用、需要重試機制的操作放到非同步程序中去執行,將你的 HTTP 服務從繁重的業務邏輯中解脫出來。以一個較低的成本將傳統 PHP 業務邏輯轉換成非阻塞、高可用、可

SpringBoot中@Async註解配合@EnableAsync註解開啟非同步任務

前言 @Async為非同步註解,放到需要使用非同步的方法上面,表示呼叫該方法的執行緒與此方法非同步執行,需要配合@EnableAsync註解使用。 沒有非同步執行,沒有@Async註解時 1、建立一個普通的類,並注入到IOC容器中 package com.ex

async/await使用

Promise 的方式雖然解決了 callback hell,但是這種方式充滿了 Promise的 then() 方法,如果處理流程複雜的話,整段程式碼將充滿 then,程式碼流程不能很好的表示執行流程。為什麼是async/await在 es6 中,我們可以使用 Genera

ES2017 中的非同步函式(async function)

非同步函式中有兩個新的關鍵字async和await async 就是非同步的意思 await 就是等的意思. 暫停函式的執行, 等待非同步任務完成. 宣告非同步函式 /*使用關鍵字 asy

C# 非同步程式設計async/await

非同步程式設計async/await詳解 1.關鍵字async 當函式使用async標記後,返回值必須為void,Task,Task<T>,當返回值為Task<T>時,函式內部只需要返回T型別,編譯器會自動包裝成Task<T>型別,如下

async await

ocs tar mozilla settime fine pro doc tin urn async await本身就是promise + generator的語法糖。 本文主要講述以下內容 async await 主要特性 async awiat 實質和

C#多線程和異步——Task和async/await

推廣 pre 問題 rect nco start 成了 logs too 閱讀目錄 一、什麽是異步 二、Task介紹 1 Task創建和運行 2 Task的阻塞方法(Wait/WaitAll/WaitAny) 3 Task的延續操作(WhenAny/W

JS中的async/await的執行順序

雖然大家知道async/await,但是很多人對這個方法中內部怎麼執行的還不是很瞭解,本文是我看了一遍技術部落格理解 JavaScript 的 async/await(如果對async/await不熟悉可以先看下http://es6.ruanyifeng.com/#docs/async)後拓展了一下,我理了一

java Io 流類

修改 文件目錄 != exe [] 深入 clas one fileinput 關於java 流類的復習;習慣性的復習按照圖結構一層層往下深入去了解去復習,最後通過代碼來實現感覺印象會更深刻一些; 關於 I/O流:IO可以理解為JAVA用來傳遞數據的管道

cookie 和session 的區別

重復 處理方式 一行 所有 有效 依據 是把 存儲 一個 二者的定義: 當你在瀏覽網站的時候,WEB 服務器會先送一小小資料放在你的計算機上,Cookie 會幫你在網站上所打的文字或是一些選擇, 都紀錄下來。當下次你再光臨同一個網站,WEB 服務器會先看看有沒有它上次留下的

cd命令使用

表示 如果 用戶家目錄 roo 環境變量 方法 字符 實用 效果   cd命令是目錄切換命令,是shell內置命令。   語法:     cd [-L|-P] [dir]   選項:     -p 如果要切換到的目標目錄是一個符號連接,直接切換到符號連接指向的目標目錄