儘管同步代碼易於追蹤和調試,但異步代碼廣泛在性能和靈活性上更具優點。Why "hold up the show" when you can trigger numerous requests at once and then handle them when each is ready?(這句要怎麼翻??)promise和許多基於promise的新的API已經成爲JavaScript世界重要的一部分。讓咱們來看一下promise的API如何來使用。javascript
XMLHttpRequest API是異步的但它並無用Promises API,如今有一些native APIs正在使用promises:html
Battery API(譯者注:這篇文章我也有翻譯)前端
fetch API(XHR的取代者)html5
ServiceWorker API(關於這個API的文章正在路上)java
promises會變得很流行,因此前端開發者們都應該去學習它。毫無疑問,Node.js是promises另外一個重要的平臺(顯然,promises是一個核心語法特性)。es6
測試 promises 比你想得容易得多,由於 setTimeout
能夠模擬你的異步「任務」json
構造函數 new Promise()
應該只用於老式的異步任務,好比 setTimeout
或者 XMLHttpRequest
。咱們使用 new
關鍵字和promise提供的 resolve
和 reject
回調函數來建立新的 promise:api
var p = new Promise(function(resolve, reject) { // Do an async task async task and then... if(/* good condition */) { resolve('Success!'); } else { reject('Failure!'); } }); p.then(function() { /* do something with the result */ }).catch(function() { /* error :( */ })
根據異步任務的返回結果,開發者能夠在回調函數體的內部手動調用 resolve
或者 reject
。把 XMLHttpRequest 轉化爲基於promise的任務就是一個實際的例子:數組
// From Jake Archibald's Promises and Back: // http://www.html5rocks.com/en/tutorials/es6/promises/#toc-promisifying-xmlhttprequest function get(url) { // Return a new promise. return new Promise(function(resolve, reject) { // Do the usual XHR stuff 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("Network Error")); }; // Make the request req.send(); }); } // Use it! get('story.json').then(function(response) { console.log("Success!", response); }, function(error) { console.error("Failed!", error); });
Sometimes you don't need to complete an async tasks within the promise -- if it's possible that an async action will be taken, however, returning a promise will be best so that you can always count on a promise coming out of a given function.(譯者注:求你們幫忙翻譯。。)這樣你就能夠簡單地調用Promise.resolve()
或者 Promise.reject()
而不須要使用 new
關鍵字。舉例說明:promise
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
方法。
全部的promise實例都有 then
方法,來對這個promise實例作進一步處理。第一個 then
方法的回調函數接收 resolve()
方法裏的值:
new Promise(function(resolve, reject) { // A mock async action using setTimeout setTimeout(function() { resolve(10); }, 3000); }) .then(function(result) { console.log(result); }); // From the console: // 10
當promise被resolved時,會調用then
回調方法。你也能夠鏈式調用 then
方法:
new Promise(function(resolve, reject) { // A mock async action using 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);}); // From the console: // first then: 10 // second then: 20 // last then: 40
每個 then
方法接收到上一個 then
方法的返回值。
若是一個promise已經被resolved,then
方法有一次被調用,那麼回調函數馬上執行。若是promise被rejected,而then
方法在rejection以後,那回調函數永遠不會被執行。
當promise被rejected時,catch
回調函數被執行:
new Promise(function(resolve, reject) { // A mock async action using setTimeout setTimeout(function() { reject('Done!'); }, 3000); }) .then(function(e) { console.log('done', e); }) .catch(function(e) { console.log('catch: ', e); }); // From the console: // 'catch: Done!'
在reject
方法裏執行什麼內容取決於你。一個常見的模式是發送一個Error
到catch
方法中:
reject(Error('Data could not be found'));
Promise.all
想一下JavaScript loaders:有許多個異步任務被同時觸發,但你只想在它們都完成以後才作出迴應---這就是Promise.all
的由來。Promise.all
方法傳入一個promise的數組,而後在它們所有resolved以後再出發回調函數:
Promise.all([promise1, promise2]).then(function(results) { // Both promises resolved }) .catch(function(error) { // One or more promises was rejected });
一個完美的想象Promise.all
方法做用的例子就是一次性出發多個AJAX(經過 fetch
):
var request1 = fetch('/users.json'); var request2 = fetch('/articles.json'); Promise.all([request1, request2]).then(function(results) { // Both promises done! });
你能夠合併都返回promise的APIs,好比fetch
和 Battery API:
Promise.all([fetch('/users.json'), navigator.getBattery()]).then(function(results) { // Both promises done! });
固然,處理rejection是困難的。若是數組中有任意一個promise被rejected,catch
方法就被觸發,而且接收第一個rejection:
var req1 = new Promise(function(resolve, reject) { // A mock async action using setTimeout setTimeout(function() { resolve('First!'); }, 4000); }); var req2 = new Promise(function(resolve, reject) { // A mock async action using 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!
Promise.race
Promise.race
是個有趣的方法---不是等待全部的promise被resolved或者rejected,而是隻要數組中有一個promise被resolved或者rejected,Promise.race
方法就被觸發:
var req1 = new Promise(function(resolve, reject) { // A mock async action using setTimeout setTimeout(function() { resolve('First!'); }, 8000); }); var req2 = new Promise(function(resolve, reject) { // A mock async action using 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.race
。
在過去的幾年中,promise一直是一個熱點話題(若是你是Dojo Toolkit用戶的話,會是過去的10年間),如今promise已經從JavaScript框架特性的級別變成了JavaScript語言的特性。你將看到會有愈來愈多的API會基於promise來實現,這是一個很好的事情!開發者們能夠避免之前的callback hell,而且異步interactions能夠像其餘的變量同樣互相傳遞。promise花了很長的時間變成如今的樣子,而如今是應該學習promise的時候了。