NodeJS的Promise的用法

Javascript的特點是異步,Javascript不能等待,如果你實現某件需要等待的事情,你不能停在那里一直等待結果回來,相反,底線是使用回調callback:你定義一個函數,這個函數只有等到結果可用時才能被調用。

這種回調模型對于好的代碼組織是沒有問題的,但是也可以通過從原始回調切換到promise解決很多問題,將promise看成是一個標準的數據容器,這樣會簡化你的代碼組織,可以成為基于promise的架構。

什么是Promise?

一個promise是一個帶有".then()"方法的對象,其代表的是一個操作的結果可能還沒有或不知道,無論誰訪問這個對象,都能夠使用".then()"方法加入回調等待操作出現成功結果或失敗時的提醒通知。

那么為什么這樣做好處優于回調呢?標準的回調模式在我們處理請求時需要同時提供回調函數:

request(url, function(error, response) {

// handle success or error.

});

doSomethingElse();

很不幸,這段代碼意味著這個request函數并不知道它自己什么時候能夠完成,當然也沒有必要,我們最終通過回調傳遞結果。這會導致多個回調形成了嵌套回調,或者稱為回調陷阱。

queryTheDatabase(query, function(error, result) {

request(url, function(error, response) {

doSomethingElse(response, function(error, result) {

doAnotherThing(result, function(error, result) {

request(anotherUrl, function(error, response) {

...

});

});

});

});

});

Promise能夠解決這種問題,允許低層代碼創建一個request然后返回一個對象,其代表著未完成的操作,讓調用者去決定應該加入什么回調。


Promise是什么?

promise是一個異步編程的抽象,它是一個返回值或拋出exception的代理對象,一般promise對象都有一個then方法,這個then方法是我們如何獲得返回值(成功實現承諾的結果值,稱為fulfillment)或拋出exception(拒絕承諾的理由,稱為rejection),then是用兩個可選的回調作為參數,我們可以稱為onFulfilled和OnRejected:

var promise = doSomethingAync()

promise.then(onFulfilled, onRejected)

當這個promise被解決了,也就是異步過程完成后,onFulfilled和OnRejected中任何一個將被調用,

因此,一個promise有下面三個不同狀態:

pending待承諾 - promise初始狀態

fulfilled實現承諾 - 一個承諾成功實現狀態

rejected拒絕承諾 - 一個承諾失敗的狀態

以讀取文件為案例,下面是使用回調實現讀取文件后應該做什么事情(輸出打印):

readFile(function (err, data) {

if (err) return console.error(err)

console.log(data)

})

如果我們的readFile函數返回一個promise,那么我們可以如下實現同樣的邏輯(輸出打印):

var promise = readFile()

promise.then(console.log, console.error)

這里我們有了一個值promise代表的是異步操作,我們能夠一直傳遞這個值promise,任何人訪問這個值都能夠使用then來消費使用它,無論這個值代表的異步操作是否完成或沒有完成,我們也能保證異步的結果不會改變,因為這個promise代表的異步操作只會執行一次,狀態是要么fulfilled要么是rejected。

理解Promise

Promise可能是不同于日常直覺,為了理解它,一些重要原理必須記牢: .then()總是返回一個新的promise.,如下面代碼:

var promise = readFile()

var promise2 = promise.then(readAnotherFile, console.error)

這里then的參數readAnotherFile, console.error是代表異步操作成功后的動作onFulfilled或失敗后的動作OnRejected,也就是說,讀取文件成功后執行readAnotherFile函數,否則失敗打印記錄錯誤。這種實現是兩個中只有一種可能。

我們再看下面上述代碼如下:

var promise = readFile()

var promise2 = promise.then(function (data) {

return readAnotherFile() // 如果readFile成功,執行readAnotherFile

}, function (err) {

console.error(err) // 如果readFile不成功,記錄,但是還是執行readAnotherFile

return readAnotherFile()

})

promise2.then(console.log, console.error) // readAnotherFile函數的執行結果

因為then返回一個promise,它意味著promise能夠被chain串行鏈條花,這樣能避免回調地獄:

readFile()

.then(readAnotherFile)

.then(doSomethingElse)

.then(...)

Promise法則有兩部分必須分離:

(1).then()總是返回一個新的promise,每次你調用它,它不管回調做什么,因為.then()在回調被調用之前已經給了你一個承諾promise,回調的行為只影響承諾promise的實施,如果回調返回一個值,那么promise將使用那個值,如果這個值是一個promise,返回這個promise實施后的值給這個值,如果回調拋出錯誤,promise將拒絕錯誤。

(2)被.then()返回的promise是一個新的promise,它不同于那些.then()被調用的promise,promise長長的鏈條有時會好些隱藏這個事實,不管如何,每次.then()調用都會產生一個新的promise,這里必須注意的是你真正需要考慮的是你最后調用.then()可能代表失敗,那么如果你不捕獲這種失敗,那么容易導致你的錯誤exception消失。

一些人認為.then()串聯鏈條調用很類似fluent風格,但是長長的promise鏈條會讓人迷惑,最后切分為一個個有意義的函數:

function getTasks() {

return $http.get('http://example.com/api/v1/tasks')

.then(function(response) {

return response.data;

});

}

function getMyTasks() {

return getTasks()

.then(function(tasks) {

return filterTasks(tasks, {

owner: user.username

});

});

}

在這個例子中,兩個函數各自獲得一個promise,攜帶了一個回調函數。

有趣的Promise

同樣的promise能夠接受任何數目的回調,當一個Promise被解決實施后,其中所有回調函數都會被調用,此外,一個promise在被解決實施后,甚至可以接受一個新的回調,這些回調完成能以正常方式被調用,這就允許我們使用回調實現簡單形式的緩存:

var tasksPromise;

function getTasks() {

taskPromise = taskPromise || getTasksFromTheServer();

return taskPromise;

}

這個案例中,getTasks()函數可以被任意次數調用,它總是返回銅牙的promise,其中函數getTasksFromTheServer()卻只是被調用一次。

Node.js error-first回調模式

在Node.js中使用Javascript Generators

Generator與Fiber比較

學習Javascript ES6幾個重要特性

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 227,663評論 6 531
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 98,125評論 3 414
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 175,506評論 0 373
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 62,614評論 1 307
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 71,402評論 6 404
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 54,934評論 1 321
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,021評論 3 440
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,168評論 0 287
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 48,690評論 1 333
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 40,596評論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 42,784評論 1 369
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,288評論 5 357
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,027評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,404評論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,662評論 1 280
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,398評論 3 390
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 47,743評論 2 370

推薦閱讀更多精彩內容