在上篇文章裏《JavaScript基礎——回調(callback)是什麼?》咱們一塊兒學習了回調,明白了回調就是一個在另一個函數執行完後要執行的函數,若是咱們但願異步函數可以像同步函數那樣順序執行,只能嵌套使用回調函數,過多的回調嵌套會使得代碼變得難以理解與維護,爲了不「回調地獄」讓人發狂的行爲,ES6原生引入了Promise的模式,經過這種方式,讓咱們代碼看起來像同步代碼,大大簡化了異步編程,簡直是ES6新特性中最讓咱們興奮的特性之一。ajax
什麼是promise?編程
首先咱們看看promise這個單詞的中文釋義,做爲名詞解釋爲承諾、諾言、誓言、約言,從中文釋義能夠看出,是一個未發生,未來必定會發生的某種東東…… 接下來咱們來看看ECMA委員會怎麼定義Promise的:數組
A Promise is an object that is used as a placeholder for the eventual results of a deferred (and possibly asynchronous) computation.promise
Promise是一個對象,用做佔位符,用於延遲(多是異步)計算的最終結果。異步
簡單的來講,Promise就是裝載將來值的容器。其實生活中有不少Promise的場景,設想如下的場景:咱們去快餐店點餐,你點了一份牛肉麪,你掃碼付款後,會拿到一份帶訂單號的收據。訂單號就是快餐店給咱們的一份牛肉麪的承諾(promise),保證了你會獲得一份牛肉麪。async
因此咱們必定要保管好咱們的訂單收據,由於咱們知道了這個收據表明了咱們將來會有一份牛肉麪,儘管快餐店不能立刻給咱們一份牛肉麪,可是咱們大腦潛意識的把訂單收據當作牛肉麪的「佔位符」了。ide
終於,咱們聽到服務員在喊「100號的牛肉麪好了,請到窗口取餐」,而後咱們拿着訂單收據來到窗口遞給服務員,咱們換來了牛肉麪。異步編程
說了不少,簡單描述這個概念就是一旦咱們須要的值準備好了,咱們就用對個人承諾值換取這個值自己。函數
可是,還有一種很差的結果,服務員叫到咱們的訂單號,當咱們去拿的時候,服務員會一臉歉意的告訴咱們「十分抱歉,您的牛肉麪賣完了」。做爲顧客的咱們對這個狀況,除了憤怒以外只能換個地吃飯了或者點其它的。從中咱們能夠看出,將來值還有一個重要的特性:它可能成功也可能失敗。post
生活的例子很簡單,咱們都經歷過,咱們是否是特別着急如何用Promise呢?在使用以前,咱們仍是先了解下——Promise State(承諾狀態,注:暫且這麼翻譯,小編也不知道如何翻譯更好)
Promise State(承諾狀態)
Promise只會處在如下狀態之一:
Pending(待處理): promise初始化的狀態,正在運行,既未完成也沒有失敗的狀態,此狀態能夠遷移至fulfilled和rejected狀態。
Fulfilled(已完成):若是回調函數實現Promise的resolve回調(稍後介紹),那咱們的promise實現兌現。
Rejected(已拒絕):若是Promise調用過程當中遭到拒絕或者發生異常,那麼咱們的promise被拒絕,處於Rejected(狀態)。
Settled(不變的,暫且這麼翻譯):Promise若是不處在Pending狀態,狀態就會改變,要不是Fulfilled要不是Rejected這兩種狀態。
Promise的狀態轉換,能夠用下面一張圖進行表示(圖片來源:https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise#Methods)
Promise vs callback
好比咱們有個需求,須要經過AJAX實現三個請求,第二個和第三個請求都依賴上一個接口的請求,若是使用CallBack的方式,咱們的代碼多是這樣的:
ajaxCallajaxCallajaxCallconsole
你們很快就會發現,這種多重嵌套的代碼不但難以理解,並且難以維護,這就是著名的「回調地獄」現象。
若是使用Promise則會讓咱們的大腦更容易接受和理解,代碼顯得簡單扁平化,代碼調用以下,如何實現ajaxCallPromise稍後介紹:
ajaxCallPromiseajaxCallPromiseajaxCallPromiseconsole
你是否是以爲代碼的複雜性忽然下降,代碼看起來更簡單易讀呢,你也許會問ajaxCallPromise怎麼寫?,彆着急,接着往下看
Promise實現——(resolve, reject) 方法
要實現回調函數轉換成Promise對象,咱們須要使用Promise構造函數,在上一小節,小編展現了ajaxCallPromise函數是如何調用的,ajaxCallPromise的實現內容以下,小編實現了(resolve,reject)相應的回調函數:
PromiseajaxAsyncWithNativeAPIresCode
如何理解這段代碼呢?
首先定義ajaxCallPromise返回類型爲Promise,這意味咱們會實現一個Promise的承諾。
Promise接受兩個函數參數,resolve(成功實現承諾)和reject(異常或失敗)
resolve和reject這兩個特有的方法,會獲取對應成功或失敗的值
若是接口請求一切正常,咱們將會經過resolve函數接收返回的值
若是接口請求失敗,咱們將會經過reject回調接收失敗返回的值
再舉個簡單的例子,若是foo()和bar()函數都實現promise,咱們改怎麼寫呢?
方式一:
fooconsole
方式二(建議這種,簡單易讀)
fooconsole
.then(onFulfilled, onRejected)
Promise的then()方法容許咱們在任務完成後或拒絕失敗後執行相應的任務,該任務能夠是基於另一個事件或基於回調的異步操做。
Promise的then()方法接收兩個參數,即onFulfilled 和 onRejected 的回調,若是Promise對象完成,若是成功狀態則執行onFulfilled回調,若是異常或失敗則執行onRejected回調。
簡單的來講,onFulfilled回調接收一個參數,及所謂的將來的值,一樣 onRejected 也接收一個參數,顯示拒絕的緣由。讓咱們改動下上小節ajaxCallPromise的then()方法:
ajaxCallPromiseconsoleconsole
若是請求過程失敗,第二個函數將會執行輸出而不是第一個函數輸出。
咱們一塊兒再來看個簡單的例子,咱們在setTimeout()實現Promise回調,代碼以下:
Promiseconsole
這裏咱們在這裏實現了一個成功狀態後沒有返回成功狀態值的Promise,函數執行後,成功返回後將來值將會是 undefined.
catch(onRejected)方法
除了then()方法能夠處理錯誤和異常,使用Promise的catch()方法也能實現一樣的功能,這個方法其實並無什麼特別,只是更容易理解而已,咱們一眼就能明白是捕獲異常的操做。
catch()方法只接收一個回調函數。catch()方法的onRejected回調的調用方式與then()方法的onRejected回調相同。
還記得咱們上小節ajaxCallPromise的then()方法的實現嗎:
ajaxCallPromiseconsoleconsole
咱們還可使用catch()方法進行捕獲異常或拒絕,效果是一致的。
ajaxCallPromisethenconsolecatchconsole
Promise.resolve(value)
Promise的resolve()方法接收成功返回值並返回一個Promise對象,用於將來值的傳遞,將值傳遞給.then(onFulfilled, onRejected) 的onFulfilled回調中。resolve()方法能夠用於將將來值轉化成Promise對象,下面的一段代碼演示瞭如何使用Promise.resolve()方法:
p1 Promisep1consolePromisep1consolePromiseconsole
控制檯將會輸出如下內容:
Eden
Promise.reject(value)
Promise.reject(value)方法與上小節Promise.resolve(value)相似,惟一不一樣的是將值傳遞給.then(onFulfilled, onRejected) 的onRejected回調中,同時Promise.reject(value)主要用來進行調試。
讓咱們看看下面一段代碼如何使用Promise.reject(value)方法:
p1 Promisep1consolePromiseconsole
控制檯將輸出:
Eden
Promise.all(iterable)
該方法傳入迭代的Promise數組對象,並一塊兒返回一個Promise對象,當全部的Promise迭代對象成功返回後,整個Promise才能返回成功狀態的值。
好了,咱們一塊兒看看怎麼實現Promise.all(iterable) 方法:
p1 Promisep2 Promisearr p1p2Promisearrconsole
特別須要注意的一點,在迭代數組中,只要任意一個進入失敗狀態,那麼該方法返回的對象也會進入失敗狀態,並將那個進入失敗狀態的錯誤信息做爲本身的錯誤信息,示例代碼以下:
p1 Promisep2 Promisearr p1p2Promisearrconsole
Promise.race(iterable)
與Promise.all(iterable) 不一樣的是,Promise.race(iterable) 雖然也接收包含若干個Promise對象的可迭代對象,不一樣的是這個方法會監聽全部的Promise對象,並等待其中的第一個進入完成或失敗狀態的Promise對象,一旦有Promise對象知足,整個Promise對象將返回這個Promise對象的成功狀態或失敗狀態,下面的示例展現了返回第一個成功狀態的值:
p1 Promisep2 Promisearr p1p2Promisearrconsoleconsole
用Promise改寫上篇文章回調
看過《JavaScript基礎——回調(callback)是什麼?》文章的同窗,文章的最後咱們用回調函數實現了一個真實的業務場景——用NodeJs實現從論壇帖子列表中顯示其中的一個帖子的信息及留言列表信息,若是使用本篇文章學習到的內容,咱們如何實現呢, 代碼以下:
index.js
fs path postsUrl path__dirnamecommentsUrl path__dirnamePromisefsJSONPromisedata datapostId postsUrlconsolecommentsUrlpostComments consolepostCommentsconsole