Promise API 簡介
譯者注: 處處是回調函數,代碼很是臃腫難看, Promise 主要用來解決這種編程方式, 將某些代碼封裝於內部。javascript
Promise 直譯爲「承諾」,但通常直接稱爲 Promise;html
代碼的可讀性很是重要,由於開發人員支出通常比計算機硬件的支出要大不少倍。前端
雖然同步代碼更容易跟蹤和調試, 但異步方式卻具備更好的性能與靈活性. 怎樣在同一時刻發起多個請求, 而後分別處理響應結果? Promise 現已成爲 JavaScript 中很是重要的一個組成部分, 不少新的API都以 promise 的方式來實現。下面簡要介紹 promise, 以及相應的 API 和使用示例!html5
Promises 周邊
XMLHttpRequest 是異步API, 但不算 Promise 方式。當前使用 Promise 的原生 api 包括:java
- Battery API
- fetch API (用來替代 XHR)
- ServiceWorker API (參見後期文章!)
Promise 會愈來愈流行,因此前端開發須要快速掌握它們。固然, Node.js 是另外一個使用 Promise 的平臺(顯然, Promise 在Node中是一個核心特性)。es6
測試 promises 可能比你想象的還要容易, 由於 setTimeout
能夠用來看成異步「任務」!編程
Promise 基本用法
直接使用 new Promise()
構造函數的方式, 應該只用來處理遺留的異步任務編程, 例如 setTimeout
或者 XMLHttpRequest
。 經過 new
關鍵字建立一個新的 Promise 對象, 該對象有 resolve
(搞定!) 和 reject
(拒絕!) 兩個回調函數:json
var p = new Promise(function(resolve, reject) { // ... ... // 此處,能夠執行某些異步任務,而後... // 在回調中,或者任何地方執行 resolve/reject if(/* good condition */) { resolve('傳入成果結果信息,如 data'); } else { reject('失敗:緣由...!'); } }); p.then(function(data) { /* do something with the result */ }).catch(function(err) { /* error :( */ });
通常是由開發人員根據異步任務執行的結果,來手動調用 resolve
或者 reject
. 一個典型的例子是將 XMLHttpRequest 轉換爲基於Promise的任務:api
// 本段示例代碼來源於 Jake Archibald's Promises and Back: // http://www.html5rocks.com/en/tutorials/es6/promises/#toc-promisifying-xmlhttprequest function get(url) { // 返回一個 promise 對象. return new Promise(function(resolve, reject) { // 執行常規的 XHR 請求 var req = new XMLHttpRequest(); req.open('GET', url); req.onload = function() { // This is called even on 404 etc // so check the status if (req.status == 200) { // Resolve the promise with the response text resolve(req.response); } else { // Otherwise reject with the status text // which will hopefully be a meaningful error reject(Error(req.statusText)); } }; // Handle network errors req.onerror = function() { reject(Error("網絡出錯")); }; // Make the request req.send(); }); }; // 使用! get('story.json').then(function(response) { console.log("Success!", response); }, function(error) { console.error("Failed!", error); });
有時候在 promise 方法體中不須要執行異步任務 —— 固然,在有可能會執行異步任務的狀況下, 返回 promise 將是最好的方式, 這樣只須要給定結果處理函數就行。在這種狀況下, 不須要使用 new 關鍵字, 直接返回 Promise.resolve()
或者 Promise.reject()
便可。例如:數組
var userCache = {}; function getUserDetail(username) { // In both cases, cached or not, a promise will be returned if (userCache[username]) { // Return a promise without the "new" keyword return Promise.resolve(userCache[username]); } // Use the fetch API to get the information // fetch returns a promise return fetch('users/' + username + '.json') .then(function(result) { userCache[username] = result; return result; }) .catch(function() { throw new Error('Could not find user: ' + username); }); };
由於老是會返回 promise, 因此只須要經過 then
和 catch
方法處理結果便可!
then
每一個 promise 實例都有 then
方法, 用來處理執行結果。 第一個 then
方法回調的參數, 就是 resolve()
傳入的那個值:
new Promise(function(resolve, reject) { // 經過 setTimeout 模擬異步任務 setTimeout(function() { resolve(10); }, 3000); }) .then(function(result) { console.log(result); }); // console 輸出的結果: // 10
then
回調由 promise 的 resolved 觸發。你也可使用鏈式的 then` 回調方法:
new Promise(function(resolve, reject) { // 經過 setTimeout 模擬異步任務 setTimeout(function() { resolve(10); }, 3000); }) .then(function(num) { console.log('first then: ', num); return num * 2; }) .then(function(num) { console.log('second then: ', num); return num * 2; }) .then(function(num) { console.log('last then: ', num);}); // console 輸出的結果: // first then: 10 // second then: 20 // last then: 40
每一個 then
收到的結果都是以前那個 then
返回的值。
若是 promise 已經 resolved, 但以後才調用 then
方法, 則當即觸發回調。若是promise被拒絕以後才調用 then
, 則回調函數不會被觸發。
catch
當 promise 被拒絕時, catch
回調就會被執行:
new Promise(function(resolve, reject) { // 經過 setTimeout 模擬異步任務 setTimeout(function() { reject('Done!'); }, 3000); }) .then(function(e) { console.log('done', e); }) .catch(function(e) { console.log('catch: ', e); }); // console 輸出的結果: // 'catch: Done!'
傳入 reject
方法的參數由你本身決定。通常來講是傳入一個 Error
對象:
reject(Error('Data could not be found'));
Promise.all
想一想JavaScript加載器的情形: 有時候會觸發多個異步交互, 但只在全部請求完成以後纔會作出響應。—— 這種狀況可使用 Promise.all
來處理。Promise.all
方法接受一個 promise 數組, 在全部 promises 都搞定以後, 觸發一個回調:
Promise.all([promise1, promise2]).then(function(results) { // Both promises resolved }) .catch(function(error) { // One or more promises was rejected });
Promise.all
的最佳示例是經過fetch
同時發起多個 AJAX請求時:
var request1 = fetch('/users.json'); var request2 = fetch('/articles.json'); Promise.all([request1, request2]).then(function(results) { // Both promises done! });
你也能夠組合使用 fetch
和 Battery 之類的 API ,由於他們都返回 promises:
Promise.all([fetch('/users.json'), navigator.getBattery()]).then(function(results) { // Both promises done! });
固然, 處理拒絕的狀況比較複雜。若是某個 promise 被拒絕, 則 catch
將會被第一個拒絕(rejection)所觸發:
var req1 = new Promise(function(resolve, reject) { // 經過 setTimeout 模擬異步任務 setTimeout(function() { resolve('First!'); }, 4000); }); var req2 = new Promise(function(resolve, reject) { // 經過 setTimeout 模擬異步任務 setTimeout(function() { reject('Second!'); }, 3000); }); Promise.all([req1, req2]).then(function(results) { console.log('Then: ', one); }).catch(function(err) { console.log('Catch: ', err); }); // From the console: // Catch: Second!
隨着愈來愈多的 API 支持 promise, Promise.all
將會變得超級有用。
Promise.race
Promise.race
是一個有趣的函數. 與 Promise.all
相反, 只要某個 priomise 被 resolved 或者 rejected, 就會觸發 Promise.race
:
var req1 = new Promise(function(resolve, reject) { // 經過 setTimeout 模擬異步任務 setTimeout(function() { resolve('First!'); }, 8000); }); var req2 = new Promise(function(resolve, reject) { // 經過 setTimeout 模擬異步任務 setTimeout(function() { resolve('Second!'); }, 3000); }); Promise.race([req1, req2]).then(function(one) { console.log('Then: ', one); }).catch(function(one, two) { console.log('Catch: ', one); }); // From the console: // Then: Second!
一個案例是請求的資源有 主站資源和備用資源(以防某個不可用)。
改變習慣, 使用 Promise
在過去幾年中 Promise 一直是個熱門話題(若是你是 Dojo Toolkit 用戶,那麼就是已經有10年了), 已經從一個JavaScript框架變成了語言的一個主要成分. 很快你就會看到大多數新的 JavaScript api 都會基於 Promise 的方式來實現……
... 固然這是一件好事! 開發人員可以避開回調的地獄, 異步交互也能夠像其餘變量同樣傳遞. Promise 還須要一段時間來普及, 如今是時候去學習他們了!
本文轉載自:衆成翻譯 譯者:鐵胖子 連接:http://www.zcfy.cc/article/351 原文:https://davidwalsh.name/promises