JavaScript有不少槽點,嵌套回調怕是千夫所指。git
好久以前,我一直使用async來處理JavaScript異步編程中的嵌套回調問題。固然我也大概的瞭解過一些其它旨在解決這些問題的類庫,諸如EventProxy、Jscex、StepJS、thenjs。github
當我第一次看到Promises規範的時候,我根本沒法理解它所帶來的好處。譬如每一個初次學習Promises的人都見過以下的示例代碼:編程
//callbacks function callback(err, value){ if(err){ // do something return; } //do other things with value } //Promises promise.then(function(value){ //do something with value }, function(err){ //do other things with error })
很難相信上面的代碼會讓人對Promises另眼相看。不過正如bluebird做者Petka所說,上面的代碼是 「最不誠實的比較」。因此我懇請你把相似的代碼從你的記憶中擦出吧。promise
不妨讓咱們再回到async的討論上。async的問題在於它不能優雅地應對需求的變化,一旦業務邏輯有較大的變化,代碼結構會進行大幅度的調整,而Promises卻可以輕鬆的應對這種變化。待時機適宜我會進行詳細的比較,首先讓咱們開始快速地瞭解Promises。異步
Promises象徵着一個異步操做的最終結果。Promises交互主要經過它的then方法,then方法接受一個回調函數,這個回調函數接受執行成功的返回值或執行失敗的錯誤緣由,錯誤緣由通常是Error對象。須要注意的是,then方法執行的返回值是一個Promise對象,而then方法接受的回調函數的返回值則能夠是任意的JavaScript對象,包括Promises。基於這種機制,Promise對象的鏈式調用就起做用了。async
Promise對象有三種狀態:pending(初始狀態)、fulfilled(成功執行)、rejected(執行出錯)。pending狀態的Promise對象能夠轉換到其它兩種狀態。異步編程
上面的文本不夠形象,不妨上些代碼來加深對Promises的認識。函數
注:因爲主流的JavaScript環境(包括NodeJS)對Promises/A+標準的實現不太使人滿意,個人示例均使用了第三方類庫bluebird。學習
var fs = require('fs') var Promise = require('bluebird') //改造fs.readFile爲Promise版本 var readFileAsync = function(path){ //返回一個Promise對象,初始狀態pending return new Promise(function(fulfill, reject){ fs.readFile(path, 'utf8', function(err, content){ //由pending狀態進入rejected狀態 if(err)return reject(err) //由pending狀態進入fulfilled狀態 return fulfill(content) }) }) } //開始使用,調用其then方法,回調接受執行成功的返回值 readFileAsync('./promise-1.js').then(function(content){ console.log(content) })
看了上面的代碼之後,是否是以爲Promises其實並不複雜呢。ui
OK,咱們繼續延續上面的代碼,來簡單比較一下傳統回調和Promises的使用上的差異: /* * 簡單比較一下傳統方式和Promises方式 * 需求:讀取兩個文件並打印內容 * */
//callbacks fs.readFile('./promise-1.js', 'utf8', function(err, content1){ //嵌套一次 console.log('#', content1) fs.readFile('./promise-1.js', 'utf8', function(err, content2){ //第二次嵌套 console.log('##', content2) }) }) //Promises readFileAsync('./promise-1.js').then(function(content1){ console.log('#', content1) //這裏返回一個Promise對象 return readFileAsync('./promiscuitye-1.js') }).then(function(content2){ console.log('##', content2) })
上面的代碼都沒有錯誤處理,這是一個後果很嚴重的壞習慣。不過今天咱們的重點不在這裏,而是分析上下兩段代碼的主要區別。
第一段代碼是傳統的嵌套回調,在第二次打印的時候已經使用了兩次縮進,而Promises鏈式調用then方法成功地避免了一次縮進(嵌套),維持了代碼結構的相對平坦。上面的代碼略顯簡陋,若是再加上錯誤處理,Promises毫無疑問將會大放光彩,有興趣請關注後續章節。
本章寫到這裏就結束了,相信你們已經對Promises的有了一個初步認識。規範文檔每每很難理解,我沒有過多的描述規範,由於我相信代碼最可以解釋一切。不過對規範文檔有興趣的能夠自行閱讀參考連接。
最後我想強調的一點就是:Promises這種維持代碼結構平坦的魔力在業務邏輯複雜多變的狀況下是很是有用的。
未完待續(2014-06-28 00:59)