原文:http://www.html5rocks.com/en/tutorials/es6/promises/
做者:Jake Archibald
翻譯:Amiojavascript
女士們先生們,請準備好迎接 Web 開發歷史上一個重大時刻……php
[鼓聲響起]html
JavaScript 有了原生的 Promise!html5
[漫天的煙花綻開,人羣沸騰了]java
這時候你大概是這三種人之一:jquery
JavaScript 是單線程的,這意味着任何兩句代碼都不能同時運行,它們得一個接一個來。在瀏覽器中,JavaScript 和其餘任務共享一個線程,不一樣的瀏覽器略有差別,但大致上這些和 JavaScript 共享線程的任務包括重繪、更新樣式、用戶交互等,全部這些任務操做都會阻塞其餘任務。git
做爲人類,你是多線程的。你能夠用多個手指同時敲鍵盤,也能夠一邊開車一遍電話。惟一的全局阻塞函數是打噴嚏,打噴嚏期間全部其餘事務都會暫停。很煩人對麼?尤爲是當你開着車打着電話的時候。咱們都不喜歡這樣打噴嚏的代碼。es6
你應該會用事件加回調的辦法來處理這類狀況:github
var img1 = document.querySelector('.img-1'); img1.addEventListener('load', function() { // woo yey image loaded }); img1.addEventListener('error', function() { // argh everything's broken });
這樣就不打噴嚏了。咱們添加幾個監聽函數,請求圖片,而後 JavaScript 就中止運行了,直到觸發某個監聽函數。ajax
上面的例子中惟一的問題是,事件有可能在咱們綁定監聽器以前就已經發生,因此咱們先要檢查圖片的complete
屬性:
var img1 = document.querySelector('.img-1'); function loaded() { // woo yey image loaded } if (img1.complete) { loaded(); } else { img1.addEventListener('load', loaded); } img1.addEventListener('error', function() { // argh everything's broken });
這樣還不夠,若是在添加監聽函數以前圖片加載發生錯誤,咱們的監聽函數仍是白費,不幸的是 DOM 也沒有爲這個需求提供解決辦法。並且,這還只是處理一張圖片的狀況,若是有一堆圖片要處理那就更麻煩了。
事件機制最適合處理同一個對象上反覆發生的事情—— keyup、touchstart 等等。你不須要考慮當綁定監聽器以前所發生的事情,當碰到異步請求成功/失敗的時候,你想要的一般是這樣:
img1.callThisIfLoadedOrWhenLoaded(function() { // loaded }).orIfFailedCallThis(function() { // failed }); // and… whenAllTheseHaveLoaded([img1, img2]).callThis(function() { // all loaded }).orIfSomeFailedCallThis(function() { // one or more failed });
這就是 Promise。若是 HTML 圖片元素有一個 ready()
方法的話,咱們就能夠這樣:
img1.ready().then(function() { // loaded }, function() { // failed }); // and… Promise.all([img1.ready(), img2.ready()]).then(function() { // all loaded }, function() { // one or more failed });
基本上 Promise 仍是有點像事件回調的,除了:
這些特性很是適合處理異步操做的成功/失敗情景,你無需再擔憂事件發生的時間點,而只需對其作出響應。
Domenic Denicola 審閱了本文初稿,給我在術語方面打了個「F」,關了禁閉而且責令我打印 States and Fates 一百遍,還寫了一封家長信給我父母。即使如此,我仍是對術語有些迷糊,不過基本上應該是這樣:
一個 Promise 的狀態能夠是:
確認(fulfilled)- 成功了
否認(rejected)- 失敗了
等待(pending)- 尚未確認或者否認,進行中
結束(settled)- 已經確認或者否認了
規範裏還使用「thenable」來描述一個對象是不是「類 Promise」(擁有名爲「then」的方法)的。這個術語使我想起來前英格蘭足球經理 Terry Venables 因此我儘可能少用它。
其實已經有一些第三方庫實現了 Promise:
上面這些庫和 JavaScript 原生 Promise 都遵照一個通用的、標準化的規範:Promises/A+,jQuery 有個相似的方法叫 Deferreds,但不兼容 Promises/A+ 規範,因而會有點小問題,使用需謹慎。jQuery 還有一個Promise 類型,但只是 Deferreds 的縮減版,因此也有一樣問題。
儘管 Promise 的各路實現遵循同一規範,它們的 API 仍是各不相同。JavaScript Promise 的 API 比較接近 RSVP.js,以下建立 Promise:
var promise = new Promise(function(resolve, reject) { // do a thing, possibly async, then… if (/* everything turned out fine */) { resolve("Stuff worked!"); } else { reject(Error("It broke")); } });
Promise 的構造器接受一個函數做爲參數,它會傳遞給這個回調函數兩個變量 resolve 和 reject。在回調函數中作一些異步操做,成功以後調用 resolve,不然調用 reject。
調用 reject 的時候傳遞給它一個 Error 對象只是個慣例並不是必須,這和經典 JavaScript 中的 throw 同樣。傳遞 Error 對象的好處是它包含了調用堆棧,在調試的時候會有點用處。
如今來看看如何使用 Promise:
promise.then(function(result) { console.log(result); // "Stuff worked!" }, function(err) { console.log(err); // Error: "It broke" });
then
接受兩個參數,成功的時候調用一個,失敗的時候調用另外一個,兩個都是可選的,因此你能夠只處理成功的狀況或者失敗的狀況。
JavaScript Promise 最初以「Futures」的名稱歸爲 DOM 規範,後來更名爲「Promises」,最終歸入 JavaScript 規範。將其加入 JavaScript 而非 DOM 的好處是方便在非瀏覽器環境中使用,如Node.js(他們會不會在覈心API中使用就是另外一回事了)。
目前的瀏覽器已經(部分)實現了 Promise。
用 Chrome 的話,就像個 Chroman 同樣裝上 Canary 版,默認即啓用了 Promise 支持。若是是 Firefox 擁躉,安裝最新的 nightly build 也同樣。
不過這兩個瀏覽器的實現都還不夠完整完全,你能夠在 bugzilla 上跟蹤 Firefox 的最新進展或者到 Chromium Dashboard 查看 Chrome 的實現狀況。
要在這兩個瀏覽器上達到兼容標準 Promise,或者在其餘瀏覽器以及 Node.js 中使用 Promise,能夠看看這個 polyfill(gzip 以後 2K)
JavaScript Promise 的 API 會把任何包含有 then
方法的對象看成「類 Promise」(或者用術語來講就是 thenable。嘆氣)的對象,這些對象通過 Promise.cast()
處理以後就和原生的 JavaScript Promise 實例沒有任何區別了。因此若是你使用的庫返回一個 Q Promise,那沒問題,無縫融入新的 JavaScript Promise。
儘管,如前所述,jQuery 的 Deferred 對象有點……沒什麼用,不過幸虧還能夠轉換成標準 Promise,你最好一拿到對象就立刻加以轉換:
var jsPromise = Promise.cast($.ajax('/whatever.json'));
這裏 jQuery 的 $.ajax
返回一個 Deferred 對象,含有 then
方法,所以 Promise.cast
能夠將其轉換爲 JavaScript Promise。不過有時候 Deferred 對象會給它的回調函數傳遞多個參數,例如:
var jqDeferred = $.ajax('/whatever.json'); jqDeferred.then(function(response, statusText, xhrObj) { // ... }, function(xhrObj, textStatus, err) { // ... });
除了第一個參數,其餘都會被 JavaScript Promise 忽略掉:
jsPromise.then(function(response) { // ... }, function(xhrObj) { // ... });
……還好這一般就是你想要的了,至少你可以用這個辦法實現想要的。另外還要注意,jQuery 也沒有遵循給否認回調函數傳遞 Error 對象的慣例。
OK,如今咱們來寫點實際的代碼。假設咱們想要:
……這個過程當中若是發生什麼錯誤了要通知用戶,而且把加載指示停掉,否則它就會不停轉下去,使人眼暈,或者搞壞界面什麼的。
固然了,你不會用 JavaScript 去這麼繁瑣地顯示一篇文章,直接輸出 HTML 要快得多,不過這個流程是很是典型的 API 請求模式:獲取多個數據,當它們所有完成以後再作一些事情。
首先搞定從網絡加載數據的步驟:
只要能保持向後兼容,現有 API 都會更新以支持 Promise,XMLHttpRequest 是重點考慮對象之一。不過如今咱們先來寫個 GET 請求:
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(); }); }
而後調用它:
get('story.json').then(function(response) { console.log("Success!", response); }, function(error) { console.error("Failed!", error); });
點擊這裏查看代碼運行頁面,打開控制檯查看輸出結果。如今咱們能夠直接發起 HTTP 請求而不須要手敲 XMLHttpRequest,這樣感受好多了,能少看一次這個狂駝峯命名的 XMLHttpRequest 我就多快樂一點。
「then」的故事還沒完,你能夠把這些「then」串聯起來修改結果或者添加進行更多異步操做。
你能夠對結果作些修改而後返回一個新值:
var promise = new Promise(function(resolve, reject) { resolve(1); }); promise.then(function(val) { console.log(val); // 1 return val + 2; }).then(function(val) { console.log(val); // 3 });
回到前面的代碼:
get('story.json').then(function(response) { console.log("Success!", response); });
收到的響應是一個純文本的 JSON,咱們能夠修改 get 函數,設置 responseType 要求服務器以 JSON 格式提供響應,不過仍是用 Promise 的方式來搞定吧:
get('story.json').then(function(response) { return JSON.parse(response); }).then(function(response) { console.log("Yey JSON!", response); });
既然 JSON.parse
只接收一個參數,並返回轉換後的結果,咱們還能夠再精簡一點:
get('story.json').then(JSON.parse).then(function(response) { console.log("Yey JSON!", response); });
點擊這裏查看代碼運行頁面,打開控制檯查看輸出結果。事實上,咱們能夠把 getJSON
函數寫得超級簡單:
function getJSON(url) { return get(url).then(JSON.parse); }
getJSON
會返回一個獲取 JSON 並加以解析的 Promise。
你也能夠把 then
串聯起來依次執行異步操做。
當你從 then
的回調函數返回的時候,這裏有點小魔法。若是你返回一個值,它就會被傳給下一個 then
的回調;而若是你返回一個「類 Promise」的對象,則下一個 then
就會等待這個 Promise 明確結束(成功/失敗)纔會執行。例如:
getJSON('story.json').then(function(story) { return getJSON(story.chapterUrls[0]); }).then(function(chapter1) { console.log("Got chapter 1!", chapter1); });
這裏咱們發起一個對 story.json 的異步請求,返回給咱們更多 URL,而後咱們會請求其中的第一個。Promise 開始首次顯現出相較事件回調的優越性了。你甚至能夠寫一個抓取章節內容的獨立函數:
var storyPromise; function getChapter(i) { storyPromise = storyPromise || getJSON('story.json'); return storyPromise.then(function(story) { return getJSON(story.chapterUrls[i]); }) } // and using it is simple: getChapter(0).then(function(chapter) { console.log(chapter); return getChapter(1); }).then(function(chapter) { console.log(chapter); });
咱們一開始並不加載 story.json,直到第一次 getChapter,而之後每次 getChapter 的時候均可以重用已經加載完成的 story Promise,因此 story.json 只須要請求一次。Promise 好棒!
前面已經看到,「then」接受兩個參數,一個處理成功,一個處理失敗(或者說確認和否認,按 Promise 術語):
get('story.json').then(function(response) { console.log("Success!", response); }, function(error) { console.log("Failed!", error); });
你還可使用 catch
:
get('story.json').then(function(response) { console.log("Success!", response); }).catch(function(error) { console.log("Failed!", error); });
這裏的 catch 並沒有任何特殊之處,只是 then(undefined, func)
的語法糖衣,更直觀一點而已。注意上面兩段代碼的行爲不只相同,後者至關於:
get('story.json').then(function(response) { console.log("Success!", response); }).then(undefined, function(error) { console.log("Failed!", error); });
差別不大,但意義非凡。Promise 被否認以後會跳轉到以後第一個配置了否認回調的 then(或 catch,同樣的)。對於 then(func1, func2)
來講,必會調用 func1 或 func2 之一,但毫不會兩個都調用。而 then(func1).catch(func2)
這樣,若是 func1 返回否認的話 func2 也會被調用,由於他們是鏈式調用中獨立的兩個步驟。看下面這段代碼:
asyncThing1().then(function() { return asyncThing2(); }).then(function() { return asyncThing3(); }).catch(function(err) { return asyncRecovery1(); }).then(function() { return asyncThing4(); }, function(err) { return asyncRecovery2(); }).catch(function(err) { console.log("Don't worry about it"); }).then(function() { console.log("All done!"); });
這段流程很是像 JavaScript 的 try/catch 組合,try
代碼塊中發生的錯誤會徑直跳轉到 catch
代碼塊。這是上面那段代碼的流程圖(我最愛流程圖了):
綠線是確認的 Promise 流程,紅線是否認的。
Promise 的否認回調能夠由 Promise.reject()
觸發,也能夠由構造器回調中拋出的錯誤觸發:
var jsonPromise = new Promise(function(resolve, reject) { // JSON.parse throws an error if you feed it some // invalid JSON, so this implicitly rejects: resolve(JSON.parse("This ain't JSON")); }); jsonPromise.then(function(data) { // This never happens: console.log("It worked!", data); }).catch(function(err) { // Instead, this happens: console.log("It failed!", err); });
這意味着你能夠把全部 Promise 相關工做都放在構造函數的回調中進行,這樣任何錯誤都能被捕捉到而且觸發 Promise 否認。
get('/').then(JSON.parse).then(function() { // This never happens, '/' is an HTML page, not JSON // so JSON.parse throws console.log("It worked!", data); }).catch(function(err) { // Instead, this happens: console.log("It failed!", err); });
回到咱們的故事和章節,咱們用 catch 來捕捉錯誤並顯示給用戶:
getJSON('story.json').then(function(story) { return getJSON(story.chapterUrls[0]); }).then(function(chapter1) { addHtmlToPage(chapter1.html); }).catch(function() { addTextToPage("Failed to show chapter"); }).then(function() { document.querySelector('.spinner').style.display = 'none'; });
若是請求 story.chapterUrls[0]
失敗(http 500 或者用戶掉線什麼的)了,它會跳過以後全部針對成功的回調,包括 getJSON
中將響應解析爲 JSON 的回調,和這裏把第一張內容添加到頁面裏的回調。JavaScript 的執行會進入 catch
回調。結果就是前面任何章節請求出錯,頁面上都會顯示「Failed to show chapter」。
和 JavaScript 的 catch 同樣,捕捉到錯誤以後,接下來的代碼會繼續執行,按計劃把加載指示器給停掉。上面的代碼就是下面這段的非阻塞異步版:
try { var story = getJSONSync('story.json'); var chapter1 = getJSONSync(story.chapterUrls[0]); addHtmlToPage(chapter1.html); } catch (e) { addTextToPage("Failed to show chapter"); } document.querySelector('.spinner').style.display = 'none';
若是隻是要捕捉異常作記錄輸出,不打算在用戶界面上對錯誤進行反饋的話,只要拋出 Error 就好了,這一步能夠放在 getJSON
中:
function getJSON(url) { return get(url).then(JSON.parse).catch(function(err) { console.log("getJSON failed for", url, err); throw err; }); }
如今咱們已經搞定第一章了,接下來搞定全部的。
異步的思惟方式並不符合直覺,若是你以爲起步困難,那就試試先寫個同步的方法,就像這個:
try { var story = getJSONSync('story.json'); addHtmlToPage(story.heading); story.chapterUrls.forEach(function(chapterUrl) { var chapter = getJSONSync(chapterUrl); addHtmlToPage(chapter.html); }); addTextToPage("All done"); } catch (err) { addTextToPage("Argh, broken: " + err.message); } document.querySelector('.spinner').style.display = 'none';
它執行起來徹底正常!(查看示例)不過它是同步的,在加載內容時會卡住整個瀏覽器。要讓它異步工做的話,咱們用 then
把它們一個接一個串起來:
getJSON('story.json').then(function(story) { addHtmlToPage(story.heading); // TODO: for each url in story.chapterUrls, fetch & display }).then(function() { // And we're all done! addTextToPage("All done"); }).catch(function(err) { // Catch any error that happened along the way addTextToPage("Argh, broken: " + err.message); }).then(function() { // Always hide the spinner document.querySelector('.spinner').style.display = 'none'; });
那麼咱們如何遍歷章節的 URL 而且依次請求?這樣是不行的:
story.chapterUrls.forEach(function(chapterUrl) { // Fetch chapter getJSON(chapterUrl).then(function(chapter) { // and add it to the page addHtmlToPage(chapter.html); }); });
forEach
沒有對異步操做的支持,因此咱們的故事章節會按照它們加載完成的順序顯示,基本上《低俗小說》就是這麼寫出來的。咱們不寫低俗小說,因此得修正它:
咱們要把章節 URL 數組轉換成 Promise 的序列,仍是用 then
:
// Start off with a promise that always resolves var sequence = Promise.resolve(); // Loop through our chapter urls story.chapterUrls.forEach(function(chapterUrl) { // Add these actions to the end of the sequence sequence = sequence.then(function() { return getJSON(chapterUrl); }).then(function(chapter) { addHtmlToPage(chapter.html); }); });
這是咱們第一次用到 Promise.resolve
,它會依據你傳的任何值返回一個 Promise。若是你傳給它一個類 Promise 對象(帶有 then
方法),它會生成一個帶有一樣確認/否認回調的 Promise,基本上就是克隆。若是傳給它任何別的值,如 Promise.resolve('Hello')
,它會建立一個以這個值爲完成結果的 Promise,若是不傳任何值,則以 undefined 爲完成結果。
還有一個對應的 Promise.reject(val)
,會建立以你傳入的參數(或 undefined)爲否認結果的 Promise。
咱們能夠用 array.reduce 精簡一下上面的代碼:
// Loop through our chapter urls story.chapterUrls.reduce(function(sequence, chapterUrl) { // Add these actions to the end of the sequence return sequence.then(function() { return getJSON(chapterUrl); }).then(function(chapter) { addHtmlToPage(chapter.html); }); }, Promise.resolve());
它和前面的例子功能同樣,可是不須要顯式聲明 sequence
變量。reduce 回調會依次應用在每一個數組元素上,第一輪裏的「sequence」是 Promise.resolve()
,以後的調用裏「sequence」就是上次函數執行的的結果。array.reduce 很是適合用於把一組值歸併處理爲一個值,正是咱們如今對 Promise 的用法。
彙總下上面的代碼:
getJSON('story.json').then(function(story) { addHtmlToPage(story.heading); return story.chapterUrls.reduce(function(sequence, chapterUrl) { // Once the last chapter's promise is done… return sequence.then(function() { // …fetch the next chapter return getJSON(chapterUrl); }).then(function(chapter) { // and add it to the page addHtmlToPage(chapter.html); }); }, Promise.resolve()); }).then(function() { // And we're all done! addTextToPage("All done"); }).catch(function(err) { // Catch any error that happened along the way addTextToPage("Argh, broken: " + err.message); }).then(function() { // Always hide the spinner document.querySelector('.spinner').style.display = 'none'; });
運行示例看這裏,前面的同步代碼改形成了徹底異步的版本。咱們還能夠更進一步,如今頁面加載的效果是這樣:
瀏覽器很擅長同時加載多個文件,咱們這種一個接一個下載章節的方法很是不效率。咱們但願同時下載全部章節,所有完成後一次搞定,正好就有這麼個 API:
Promise.all(arrayOfPromises).then(function(arrayOfResults) { //... });
Promise.all
接受一個 Promise 數組爲參數,建立一個當全部 Promise 都完成以後就完成的 Promise,它的完成結果是一個數組,包含了全部先前傳入的那些 Promise 的完成結果,順序和將它們傳入的數組順序一致。
getJSON('story.json').then(function(story) { addHtmlToPage(story.heading); // Take an array of promises and wait on them all return Promise.all( // Map our array of chapter urls to // an array of chapter json promises story.chapterUrls.map(getJSON) ); }).then(function(chapters) { // Now we have the chapters jsons in order! Loop through… chapters.forEach(function(chapter) { // …and add to the page addHtmlToPage(chapter.html); }); addTextToPage("All done"); }).catch(function(err) { // catch any error that happened so far addTextToPage("Argh, broken: " + err.message); }).then(function() { document.querySelector('.spinner').style.display = 'none'; });
根據鏈接情況,改進的代碼會比順序加載方式提速數秒,甚至代碼行數也更少。章節加載完成的順序不肯定,但它們顯示在頁面上的順序準確無誤。
然而這樣仍是有提升空間。當第一章內容加載完畢咱們能夠當即填進頁面,這樣用戶能夠在其餘加載任務還沒有完成以前就開始閱讀;當第三章到達的時候咱們不動聲色,第二章也到達以後咱們再把第二章和第三章內容填入頁面,以此類推。
爲了達到這樣的效果,咱們同時請求全部的章節內容,而後建立一個序列依次將其填入頁面:
getJSON('story.json').then(function(story) { addHtmlToPage(story.heading); // Map our array of chapter urls to // an array of chapter json promises. // This makes sure they all download parallel. return story.chapterUrls.map(getJSON) .reduce(function(sequence, chapterPromise) { // Use reduce to chain the promises together, // adding content to the page for each chapter return sequence.then(function() { // Wait for everything in the sequence so far, // then wait for this chapter to arrive. return chapterPromise; }).then(function(chapter) { addHtmlToPage(chapter.html); }); }, Promise.resolve()); }).then(function() { addTextToPage("All done"); }).catch(function(err) { // catch any error that happened along the way addTextToPage("Argh, broken: " + err.message); }).then(function() { document.querySelector('.spinner').style.display = 'none'; });
哈哈(查看示例),魚與熊掌兼得!加載全部內容的時間未變,但用戶能夠更早看到第一章。
這個小例子中各部分章節加載差很少同時完成,逐章顯示的策略在章節內容不少的時候優點將會更加顯著。
上面的代碼若是用 Node.js 風格的回調或者事件機制實現的話代碼量大約要翻一倍,更重要的是可讀性也不如此例。然而,Promise 的厲害不止於此,和其餘 ES6 的新功能結合起來還能更加高效……
接下來的內容涉及到一大堆 ES6 的新特性,不過對於如今應用 Promise 來講並不是必須,把它看成接下來的第二部豪華續集的預告片來看就行了。
ES6 還給咱們帶來了 Generator,容許函數在特定地方像 return 同樣退出,可是稍後又能恢復到這個位置和狀態上繼續執行。
function *addGenerator() { var i = 0; while (true) { i += yield i; } }
注意函數名前的星號,這表示該函數是一個 Generator。關鍵字 yield 標記了暫停/繼續的位置,使用方法像這樣:
var adder = addGenerator(); adder.next().value; // 0 adder.next(5).value; // 5 adder.next(5).value; // 10 adder.next(5).value; // 15 adder.next(50).value; // 65
這對 Promise 有什麼用呢?你能夠用這種暫停/繼續的機制寫出來和同步代碼看上去差很少(理解起來也同樣簡單)的代碼。下面是一個輔助函數(helper function),咱們在 yield
位置等待 Promise 完成:
function spawn(generatorFunc) { function continuer(verb, arg) { var result; try { result = generator[verb](arg); } catch (err) { return Promise.reject(err); } if (result.done) { return result.value; } else { return Promise.cast(result.value).then(onFulfilled, onRejected); } } var generator = generatorFunc(); var onFulfilled = continuer.bind(continuer, "next"); var onRejected = continuer.bind(continuer, "throw"); return onFulfilled(); }
這段代碼原樣拷貝自 Q,只是改爲 JavaScript Promise 的 API。把咱們前面的最終方案和 ES6 最新特性結合在一塊兒以後:
spawn(function *() { try { // 'yield' effectively does an async wait, // returning the result of the promise let story = yield getJSON('story.json'); addHtmlToPage(story.heading); // Map our array of chapter urls to // an array of chapter json promises. // This makes sure they all download parallel. let chapterPromises = story.chapterUrls.map(getJSON); for (let chapterPromise of chapterPromises) { // Wait for each chapter to be ready, then add it to the page let chapter = yield chapterPromise; addHtmlToPage(chapter.html); } addTextToPage("All done"); } catch (err) { // try/catch just works, rejected promises are thrown here addTextToPage("Argh, broken: " + err.message); } document.querySelector('.spinner').style.display = 'none'; });
功能徹底同樣,讀起來要簡單得多。這個例子目前能夠在 Chrome Canary 中運行(查看示例),不過你得先到 about:flags 中開啓 Enable experimental JavaScript 選項。
這裏用到了一堆 ES6 的新語法:Promise、Generator、let、for-of。當咱們把 yield
應用在一個 Promise 上,spawn 輔助函數會等待 Promise 完成,而後才返回最終的值。若是 Promise 給出否認結果,spawn 中的 yield 則會拋出一個異常,咱們能夠用 try/catch 捕捉到。這樣寫異步代碼真是超級簡單!
除非額外註明,最新版的 Chrome(Canary) 和 Firefox(nightly) 均支持下列全部方法。這個 Polyfill 則在全部瀏覽器內實現一樣的接口。
Promise.cast(promise);
返回一個 Promise(當且僅當 promise.constructor == Promise)
備註:目前僅有 Chrome 實現
Promise.cast(obj);
建立一個以 obj 爲成功結果的 Promise。
備註:目前僅有 Chrome 實現
Promise.resolve(thenable);
從 thenable 對象建立一個新的 Promise。一個 thenable(類 Promise)對象是一個帶有「then」方法的對象。若是你傳入一個原生的 JavaScript Promise 對象,則會建立一個新的 Promise。此方法涵蓋了 Promise.cast 的特性,可是不如 Promise.cast 更簡單高效。
Promise.resolve(obj);
建立一個以 obj 爲確認結果的 Promise。這種狀況下等同於 Promise.cast(obj)。
Promise.reject(obj);
建立一個以 obj 爲否認結果的 Promise。爲了一致性和調試便利(如堆棧追蹤),obj 應該是一個 Error 實例對象。
Promise.all(array);
建立一個 Promise,當且僅當傳入數組中的全部 Promise 都確認以後才確認,若是遇到數組中的任何一個 Promise 以否認結束,則拋出否認結果。每一個數組元素都會首先通過 Promise.cast,因此數組能夠包含類 Promise 對象或者其餘對象。確認結果是一個數組,包含傳入數組中每一個 Promise 的確認結果(且保持順序);否認結果是傳入數組中第一個遇到的否認結果。
備註:目前僅有 Chrome 實現
Promise.race(array);
建立一個 Promise,當數組中的任意對象確認時將其結果做爲確認結束,或者當數組中任意對象否認時將其結果做爲否認結束。
備註:我不大肯定這個接口是否有用,我更傾向於一個 Promise.all 的對立方法,僅當全部數組元素所有給出否認的時候才拋出否認結果