JavaScript有很多槽點,嵌套回調怕是千夫所指。 很久之前,我一直使用async來處理JavaScript非同步編程中的嵌套回調問題。當然我也大概的瞭解過一些其它旨在解決這些問題的類庫,諸如EventProxy、Jscex、StepJS、thenjs。 當我第一次看到Promises規範的時候,我根本無法理解它所帶來的好處。譬如每個初次學習Promises的人都見過如下的範例程式碼: //callbacks function callback(err, value){<br> if(err){<br> // do something<br> return;<br> }<br> //do other things with value } //Promises promise.then(function(value){<br> //do something with value }, function(err){<br> //do other things with error })很難相信上面的代碼會讓人對Promises刮目相看。不過正如bluebird作者Petka所說,上面的代碼是 “最不誠實的比較”。所以我懇請你把類似的代碼從你的記憶中擦出吧。 不妨讓我們再回到async的討論上。async的問題在於它不能優雅地應對需求的變化,一旦商務邏輯有較大的變化,代碼結構會進行大幅度的調整,而Promises卻能夠輕鬆的應對這種變化。待時機適宜我會進行詳細的比較,首先讓我們開始快速地瞭解Promises。 Promises是什麼Promises象徵著一個非同步作業的最終結果。Promises互動主要通過它的then方法,then方法接受一個回呼函數,這個回呼函數接受執行成功的傳回值或執行失敗的錯誤原因,錯誤原因一般是Error對象。需要注意的是,then方法執行的傳回值是一個Promise對象,而then方法接受的回呼函數的傳回值則可以是任意的JavaScript對象,包括Promises。基於這種機制,Promise對象的鏈式調用就起作用了。 Promises的狀態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其實並不複雜呢。 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的有了一個初步認識。規範文檔往往很難理解,我沒有過多的描述規範,因為我相信代碼最能夠解釋一切。不過對規範文檔有興趣的可以自行閱讀參考連結。