Promise 簡介
譯者注: 到處是回撥函式,程式碼非常臃腫難看, Promise 主要用來解決這種程式設計方式, 將某些程式碼封裝於內部。
Promise 直譯為“承諾”,但一般直接稱為 Promise;
程式碼的可讀性非常重要,因為開發人員支出一般比計算機硬體的支出要大很多倍。
雖然同步程式碼更容易跟蹤和除錯, 但非同步方式卻具有更好的效能與靈活性.
怎樣在同一時刻發起多個請求, 然後分別處理響應結果? Promise 現已成為 JavaScript 中非常重要的一個組成部分, 很多新的API都以 promise 的方式來實現。下面簡要介紹 promise, 以及相應的 API 和使用示例!
Promises 周邊
XMLHttpRequest 是非同步API, 但不算 Promise 方式。當前使用 Promise 的原生 api 包括:
- Battery API
- fetch API (用來替代 XHR)
- ServiceWorker API (參見後期文章!)
Promise 會越來越流行,所以前端開發需要快速掌握它們。當然, Node.js 是另一個使用 Promise 的平臺(顯然, Promise 在Node中是一個核心特性)。
測試 promises 可能比你想象的還要容易, 因為 setTimeout
可以用來當作非同步“任務”!
Promise 基本用法
直接使用 new Promise()
建構函式的方式, 應該只用來處理遺留的非同步任務程式設計, 例如 setTimeout
或者 XMLHttpRequest
。 通過 new
關鍵字建立一個新的 Promise 物件, 該物件有 resolve
(搞定!) 和 reject
(拒絕!) 兩個回撥函式:
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的任務:
// 本段示例程式碼來源於 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://zcfy.cc/article/351
翻譯人員: 鐵錨 http://blog.csdn.net/renfufei
翻譯時間: 2016年6月25日
原文時間: 2015年11月2日
相關文章
- Promise的簡單介紹Promise
- JS非同步之callback、promise、async+await簡介JS非同步PromiseAI
- 學習Promise && 簡易實現PromisePromise
- 簡單理解promisePromise
- 簡單易懂的PromisePromise
- Promise 簡單實現Promise
- 手寫簡易PromisePromise
- Promise的簡單用法Promise
- ES6 Promise介紹Promise
- Promise 原始碼:實現一個簡單的 PromisePromise原始碼
- 簡單版Promise實現Promise
- Promise 其實很簡單Promise
- Promise的簡單實現Promise
- promise原理就是這麼簡單Promise
- [Javascript] Promise ES6 詳細介紹JavaScriptPromise
- Promise 基本方法的簡單實現Promise
- 精簡的非同步Promise實現非同步Promise
- Promise的使用及簡單實現Promise
- 簡介
- Jira使用簡介 HP ALM使用簡介
- 來了老弟,最簡單的Promise原理Promise
- 簡單聊聊ES6-Promise和AsyncPromise
- future promise shared_future簡單使用Promise
- 自己實現一個簡單的 PromisePromise
- 使用 Promise.withResolvers() 來簡化你將函式 Promise 化的實現~~Promise函式
- BookKeeper 介紹(1)--簡介
- Amphenol簡介
- Vagrant簡介
- PySimpleGUI 簡介GUI
- Protobuf簡介
- MyBatis 簡介MyBatis
- jango簡介Go
- cookie 簡介Cookie
- Session 簡介Session
- Cookie簡介Cookie
- Virgilio 簡介
- Django簡介Django
- ElasticSearch簡介Elasticsearch