Async介紹
Async是一個流程控制工具包,提供了直接而強大的異步功能。基于Javascript為Node.js設計,同時也可以直接在瀏覽器中使用。
Async提供了大約20個函數,包括常用的 map, reduce, filter, forEach 等,異步流程控制模式包括,串行(series),并行(parallel),瀑布(waterfall)等。
項目地址:https://github.com/caolan/async
Async安裝
npm install async(npm是隨同NodeJS一起安裝的包管理工具)
Async函數介紹
async主要實現了三個部分的流程控制功能:
- 集合: Collections
- 流程控制: Control Flow
- 工具類: Utils
1). 集合: Collections
- each: 如果想對同一個集合中的所有元素都執行同一個異步操作。
- map: 對集合中的每一個元素,執行某個異步操作,得到結果。所有的結果將匯總到最終的callback里。與each的區別是,each只關心操作不管最后的值,而map關心的最后產生的值。
- filter: 使用異步操作對集合中的元素進行篩選, 需要注意的是,iterator的callback只有一個參數,只能接收true或false。
- reject: reject跟filter正好相反,當測試為true時則拋棄
- reduce: 可以讓我們給定一個初始值,用它與集合中的每一個元素做運算,最后得到一個值。reduce從左向右來遍歷元素,如果想從右向左,可使用reduceRight。
- detect: 用于取得集合中滿足條件的第一個元素。
- sortBy: 對集合內的元素進行排序,依據每個元素進行某異步操作后產生的值,從小到大排序。
- some: 當集合中是否有至少一個元素滿足條件時,最終callback得到的值為true,否則為false.
- every: 如果集合里每一個元素都滿足條件,則傳給最終回調的result為true,否則為false
- concat: 將多個異步操作的結果合并為一個數組。
2). 流程控制: Control Flow
- series: 串行執行,一個函數數組中的每個函數,每一個函數執行完成之后才能執行下一個函數。
- parallel: 并行執行多個函數,每個函數都是立即執行,不需要等待其它函數先執行。傳給最終callback的數組中的數據按照tasks中聲明的順序,而不是執行完成的順序。
- whilst: 相當于while,但其中的異步調用將在完成后才會進行下一次循環。
- doWhilst: 相當于do…while, doWhilst交換了fn,test的參數位置,先執行一次循環,再做test判斷。
- until: until與whilst正好相反,當test為false時循環,與true時跳出。其它特性一致。
- doUntil: doUntil與doWhilst正好相反,當test為false時循環,與true時跳出。其它特性一致。
- forever: 無論條件循環執行,如果不出錯,callback永遠不被執行。
- waterfall: 按順序依次執行一組函數。每個函數產生的值,都將傳給下一個。
- compose: 創建一個包括一組異步函數的函數集合,每個函數會消費上一次函數的返回值。把f(),g(),h()異步函數,組合成f(g(h()))的形式,通過callback得到返回值。
- applyEach: 實現給一數組中每個函數傳相同參數,通過callback返回。如果只傳第一個參數,將返回一個函數對象,我可以傳參調用。
- queue: 是一個串行的消息隊列,通過限制了worker數量,不再一次性全部執行。當worker數量不夠用時,新加入的任務將會排隊等候,直到有新的worker可用。
- cargo: 一個串行的消息隊列,類似于queue,通過限制了worker數量,不再一次性全部執行。不同之處在于,cargo每次會加載滿額的任務做為任務單元,只有任務單元中全部執行完成后,才會加載新的任務單元。
- auto: 用來處理有依賴關系的多個任務的執行。
- iterator: 將一組函數包裝成為一個iterator,初次調用此iterator時,會執行定義中的第一個函數并返回第二個函數以供調用。
- apply: 可以讓我們給一個函數預綁定多個參數并生成一個可直接調用的新函數,簡化代碼。
- nextTick: 與nodejs的nextTick一樣,再最后調用函數。
- times: 異步運行,times可以指定調用幾次,并把結果合并到數組中返回
- timesSeries: 與time類似,唯一不同的是同步執行
3). 工具類: Utils
- memoize: 讓某一個函數在內存中緩存它的計算結果。對于相同的參數,只計算一次,下次就直接拿到之前算好的結果。
- unmemoize: 讓已經被緩存的函數,返回不緩存的函數引用。
- log: 執行某異步函數,并記錄它的返回值,日志輸出。
- dir: 與log類似,不同之處在于,會調用瀏覽器的console.dir()函數,顯示為DOM視圖。
- noConflict: 如果之前已經在全局域中定義了async變量,當導入本async.js時,會先把之前的async變量保存起來,然后覆蓋它。僅僅用于瀏覽器端,在nodejs中沒用,這里無法演示。
以上所有函數需要配合例子才能深入理解,可前往github網站下載demo:https://github.com/bsspirit/async_demo
每個函數的用法,有非常詳細的實例,相信通過例子你會更好的理解流程控制!
對數據庫的連續操作
下面我們通過一個實際工作中會遇到的一個場景來具體講解流程控制: Control Flow這一部分。
假如我們的過程是先從數據庫刪除記錄,刪除后再插入一套記錄,插入后查詢數據庫,查詢出結果后再更新......具體代碼可以看上一篇《nodejs進階(6)—連接MySQL數據庫》在文章最后一段。為了實現了串行操作,所有的調用都是在callback中實現的,5層嵌套結構。這種代碼已經變得不可以維護了。所以,需要用async庫,對上面的代碼結構進行重寫!
1 var OptPool = require('./models/mysqlPool'); 2 var async = require('async'); 3 var optPool = new OptPool(); 4 var pool = optPool.getPool(); 5 var sqls = { 6 'insertSQL': 'insert into user(name) values("conan"),("fens.me")', 7 'selectSQL': 'select * from user limit 10', 8 'deleteSQL': 'delete from user', 9 'updateSQL': 'update user set name="conan update" where name="conan"' 10 }; 11 12 var tasks = ['deleteSQL', 'insertSQL', 'selectSQL', 'updateSQL', 'selectSQL']; 13 14 pool.getConnection(function(err,conn){ 15 async.eachSeries(tasks, function (item, callback) { 16 console.log(item + " ==> " + sqls[item]); 17 conn.query(sqls[item], function (err, res) { 18 console.log(res); 19 callback(err, res); 20 }); 21 }, function (err) { 22 console.log("err: " + err); 23 }); 24 })
運行結果:
雖然跟上一篇博文中《nodejs進階(6)—連接MySQL數據庫》實現的功能是一樣的,但代碼的可讀性就增強了許多倍,這就是高效的開發。
文章列表