摘要:所謂異步編程中的異步是相對于同步的概念的。是一系列異步編程規范的統稱。如果中的回調函數返回一個值,那么返回的將會成為接受狀態,并且將返回的值作為接受狀態的回調函數的參數值。參考介紹基礎篇深入理解與異步編程。
es6 promise與異步編程
對于一些還不具備大量編程經驗的朋友來說,promise可能是es6比較難以掌握的點。首先是很多名詞,比如Promises,es6 Promise, 回調函數(callback),Promise/A+,異步編程等。下面就首先介紹下這些名詞的含義和區別。
所謂異步編程中的異步是相對于同步的概念的。js是單線程的語言,同一時間只能做一件事,為了指定一些稍后要執行的代碼,我們需要異步。在客戶端,主要的異步方式有事件,setTimeout,Ajax等。Node的發展大大擴展了js語言的邊界,我們知道,Node使用非阻塞IO模型,它使用回調函數模式來實現異步編程。比如:
readFile("example.txt", function(err, contents){ if(err){ throw err; } console.log(contents); }); console.log("Hi!");
上面代碼中readFile的第二個參數就是回調函數。它會在讀取完example.txt后被添加到執行隊列中。上面代碼的執行順序是--執行readFile函數,在遇到讀取文件時暫停,打印"Hi",讀取文件結束后將回調添加到作業隊列中,執行回調函數,打印contents。
本來呢,使用回調函數是能夠完成異步編程的。但是隨著代碼的邏輯越復雜,這種異步編程方式越來越難以閱讀和追蹤程序錯誤,所以發展出了Promises規范來完成異步編程。
Promises是一系列異步編程規范的統稱。我們需要了解的是其中的Promise/A+規范。es6通過Promise這個內建對象實現了該規范。所以我們可以使用es6中的Promise對象來進行異步編程。
下面將對es6中的Promise對象進行介紹。至于jQuery中延遲對象$.deferred(),根據規范自己實現promise和ES7的Async/Await異步方式等更多內容,后面會專門寫一篇文章進行介紹。
語法 Promise的3種狀態一個promise實例有3種狀態,分別是:
pending -- 掛起,表示Promise結果還未知。
fulfilled -- 已完成, 表示Promise成功完成。
rejected -- 已拒絕,表示Promise未成功結束。
promise處于這3種狀態中的一種,并且可以由pending狀態變為fulfilled狀態,或由pending變為rejected狀態。反之則不行。
為了便于理解,下面將通過一個生活化的例子,來解釋什么是Promise?
Promise是允諾的意思。它就是一個關于未發生的事情的承諾。比如:
你訂了一份燒烤,店家說半個小時內送到,這就是一個Promise。現在,這個Promise還沒有發生,所以可能半個小時內配送成功或者失敗。對此,你預備了兩種處理方式:成功 -- 美滋滋的吃燒烤,失敗 -- 去樓下店里吃。
在半個小時內,這個Promise處于pending狀態,你正常上網,擼代碼。一段時間后,這個promise就有了結果。是成功(fulfilled)或者失敗(rejected)。根據這個結果,你之前的兩種處理方式就會相應執行。這就是promise。
對應的代碼如下:
let promise = new Promise(function(resolve, reject){ //等待店家送來中... let result = "配送成功"? true : false; if(result){ resolve(value); }else{ reject(reason); } }); promise.then(function(value){ //美滋滋吃燒烤... //value為上面resolve()中傳遞的值, 比如共100塊錢。 }, function(reason){ //叫上隔壁老王去樓下吃... //reason為上面reject()的傳遞的原因,比如烤糊了... });
上面代碼就是通過promise異步編程的代碼。這里要注意的是Promise構造函數接收一個函數作為參數,函數內部是異步的邏輯。這個函數接收兩個參數:resolve和reject。resolve()可以把promise推向fulfilled狀態,reject()可以把promise推向rejected狀態。
promise有個then方法,用于處理promise成功或失敗后的邏輯。then有兩個參數:
參數1為promise成功時執行的函數,該函數的參數value對應于上面resolve(value)中的value值;
參數2為promise失敗時執行的函數,該函數的參數reason對應于reject(reason)中的reason值,表示失敗的原因。
一旦promise有了結果(成功或失敗),就會執行對應then中的函數。
Ajax是客戶端最常用的異步編程場景,下面一個例子演示了使用Promise進行Ajax操作的代碼。
function getData(method, url){ let promise = new Promise(function(resolve, reject){ let xmlHttp = new XMLHttpRequest(); xmlHttp.open(method, url); xmlHttp.send(); xmlHttp.onload = function () { if (this.status == 200 ) { resolve(this.response); } else { reject(this.statusText); } }; xmlHttp.onerror = function () { reject(this.statusText); }; }) return promise; } getData("get","www.xxx.com").then(successFun, failFun); function successFun(value){ //Ajax成功處理函數... } function failFun(reason){ //Ajax失敗處理函數... }創建一個已決的Promise
前面的例子promise創建時,promise都處于pending狀態,根據異步操作的結果將promise推向成功或失敗狀態。
Promise類型有兩個靜態方法Promise.resolve(value),Promise.reject(reason)可以分別創建已經是fulfilled和已經是rejected狀態的promise。
比如:
let promise = Promise.resolve(44); promise.then(function(value){ console.log("fulfilled", value); })
上面代碼promise在被創建出來時,已經是fulfilled狀態,接下來會直接將then中的回調函數加入到作業隊列中,等待作業隊列中前面的任務完成后執行該函數。
這里傳入Promise.resolve(value)和Promise.reject(reason)中的參數和之前Promise構造是對應的參數是一樣的。
Promise.prototype.then()和Promise.prototype.catch()上面已經演示過promise實例上then方法的用法,每一個promise實例還具有catch方法。
catch()方法只處理reject的情況,他的行為與調用Promise.prototype.then(undefined, onRejected)相同。比如:
let p = new Promise(function(resolve, reject){ //... reject(new Error("something wrong!")) }) p.catch(function(reason){ //拒絕 })
上面catch方法中的回調在promise被reject時調用。
then()和catch()的返回值每次對then()或catch()的調用都會返回另一個promise,這也是很多代碼可以寫成類似鏈式調用的原因。比如:
let p1 = new Promise(function(resolve, reject){ resolve(42); }); let p2 = p1.then(function(value){ console.log(value); }) p2.then(function(){ console.log("Finished"); }, function(){ console.log("something wrong!"); }); p1 == p2 // false,注意:p1.then()會返回一個新的promise,所以p1與p2并不相等 //可以寫成鏈式調用的形式,比如 p1.then(function(value){ console.log(value) }).then(function(){ console.log("do something"); }).then(function(){ console.log("Finished"); })
在上面代碼中,p1.then()返回了一個promise為p2, 那么p2的狀態和p1之間有什么關系呢?
更具體一點說,當p1變為fulfilled時,p1.then()返回的p2是什么狀態呢?二者有什么聯系呢?
p2的行為與p1.then()中回調函數的返回值有關:
如果then中的回調函數拋出一個錯誤,或者回調函數中調用reject(reason),那么then返回的Promise將會成為拒絕狀態,并且將拋出的錯誤作為拒絕狀態的回調函數的參數值。
如果then中的回調函數返回一個值,那么then返回的Promise將會成為接受狀態,并且將返回的值作為接受狀態的回調函數的參數值。
如果then中的回調函數返回一個已經是接受狀態的Promise,那么then返回的Promise也會成為接受狀態,并且將那個Promise的接受狀態的回調函數的參數值作為該被返回的Promise的接受狀態回調函數的參數值。
如果then中的回調函數返回一個已經是拒絕狀態的Promise,那么then返回的Promise也會成為拒絕狀態,并且將那個Promise的拒絕狀態的回調函數的參數值作為該被返回的Promise的拒絕狀態回調函數的參數值。
如果then中的回調函數返回一個未定狀態(pending)的Promise,那么then返回Promise的狀態也是未定的,并且它的終態與那個Promise的終態相同;同時,它變為終態時調用的回調函數參數與那個Promise變為終態時的回調函數的參數是相同的。
如果then中的回調函數無顯式的返回值,并且也沒有調用reject(),那么返回的Promise為接收狀態。
Promise.all()處理多個promisePromise內建對象上的靜態方法Promise.all()用于處理多個promise的情況。
Promise.all([promise1, promise2,...])返回一個promise的實例,接收一個promise組成的數組為參數。只有當數組內的promise都成功時,才會調用對應的then中的成功處理函數,只要有一個不成功,那么調用對應的拒絕處理函數。
依然使用前面那么訂燒烤的例子,你不僅訂了燒烤,還在另一家訂了啤酒。打算等到燒烤和啤酒都配送成功后一起吃,美滋滋~~。比如:
Promise.all([訂燒烤,訂啤酒]).then(function(value){ //吃燒烤,喝啤酒... }, function(reason){ //拒絕的原因,烤糊了或者啤酒賣完了... })
這里要注意的一點是,對于數組中的promise,只要有任一個promise為拒絕,那么就會立即執行then中的拒絕處理函數,并不會等待其他promise的結果。只有當所有promise的結果都成功時,才執行then中的成功處理函數。比如:
var p1 = new Promise(function(resolve, reject){ setTimeout(function(){ console.log("A"); resolve(); }, 1000) }); var p2 = Promise.reject(new Error("error")); var p3 = new Promise(function(resolve, reject){ setTimeout(function(){ console.log("B"); resolve(); }, 0) }); Promise.all([p1,p2,p3]).then(function(value){ console.log("success!"); }, function(reason){ console.log("failed"); }) //結果為failed B A
由于p2為已拒絕狀態的promise,所以Promise.all()立即變為拒絕狀態,打印failed,p1和p2會繼續執行,但對于Promise.all()的結果沒有影響。
Promise.race()處理多個promisePromise內建對象上的靜態方法Promise.race()同樣用于處理多個promise的情況。同樣返回一個Promise,同樣接收一個promise數組作為參數。
與all不同的地方在于,數組中的promise就像在賽跑一樣(race),并且只關心第一名的情況,只要有其中一個promise有了結果,Promise.race()的狀態就會立即與該promise相同。
數組中其他promise繼續執行,但對于Promise.race()的結果沒有影響。
我們構造promise實例的代碼是立即執行的,而then方法中的回調函數是異步調用的,在promise的狀態變為成功或拒絕時,才會把相應的處理函數添加到promise工作隊列中。并且該函數會先于setTimeout執行。例如:
var promise = new Promise(function(resolve, reject){ console.log("A"); resolve("C"); }) console.log("B"); setTimeout(function(){ console.log("D"); },0) promise.then(function(value){ console.log(value) }); //打印A, B, C, D
如果then方法中傳入的參數被忽略,或者是非函數,比如:
p.then(function(value){ //... }) //或者 p.then(undefined, function(reason){ //... })
那么,相應的回調處理函數被忽略,then方法返回的promise會保留上一個promise的狀態和參數。最典型的例子:
var p = new Promise(function(resolve, reject){ reject(new Error("error")); }) p.then(function(value){ //... }).then(function(value){ //... }).then(undefined, function(reason){ console.log(reason); }) //打印"error"
p的前兩次then調用的拒絕處理函數被忽略,然后reject狀態和錯誤信息就一直往后傳遞,直到被最后一次then調用捕獲。
最佳實踐關于Promise的知識點很多,但是最常用的場景就是Ajax。比如:
function getData(method, url){ var promise = new Promise(function(resolve, reject){ //Ajax獲取數據的代碼... if(success){ resolve(response) }else{ reject(statusText) } }) return promise; } getData("get","www.xxx.com").then(Fun1).then(Fun2).then(Fun3).catch(function(reason){ //錯誤處理邏輯... });
更多關于es6的內容,可以關注右側我的專欄--學習ES6。
參考:
MDN Javascript Promise.
Promise介紹-基礎篇.
《深入理解ES6》-- Promise與異步編程。
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/93249.html
摘要:的翻譯文檔由的維護很多人說,阮老師已經有一本關于的書了入門,覺得看看這本書就足夠了。前端的異步解決方案之和異步編程模式在前端開發過程中,顯得越來越重要。為了讓編程更美好,我們就需要引入來降低異步編程的復雜性。 JavaScript Promise 迷你書(中文版) 超詳細介紹promise的gitbook,看完再不會promise...... 本書的目的是以目前還在制定中的ECMASc...
摘要:和和都有和,但是略有不同。實際上返回的是一個對象。和添加的回調,添加的回調。所以在調用成功的情況下執行添加的回調,調用失敗時執行添加的回調。,產生對象并,產生對象并,然后繼續處理,的語法糖,和的差不多但不同。 Deferred 和 Promise ES6 和 jQuery 都有 Deffered 和 Promise,但是略有不同。不過它們的作用可以簡單的用兩句話來描述 Deffere...
摘要:從最開始的到封裝后的都在試圖解決異步編程過程中的問題。為了讓編程更美好,我們就需要引入來降低異步編程的復雜性。寫一個符合規范并可配合使用的寫一個符合規范并可配合使用的理解的工作原理采用回調函數來處理異步編程。 JavaScript怎么使用循環代替(異步)遞歸 問題描述 在開發過程中,遇到一個需求:在系統初始化時通過http獲取一個第三方服務器端的列表,第三方服務器提供了一個接口,可通過...
摘要:如果有錯誤,則到的第二個回調函數中,對錯誤進行處理。假設第一個的第一個回調沒有返回一個對象,那么第二個的調用者還是原來的對象,只不過其的值變成了第一個中第一個回調函數的返回值。 ES6標準出爐之前,一個幽靈,回調的幽靈,游蕩在JavaScript世界。 正所謂: 世界本沒有回調,寫的人多了,也就有了})})})})})。 Promise的興起,是因為異步方法調用中,往往會出現回調函數一...
摘要:就算改變已經發生了,即使再對對象添加回調函數,也會立即得到這個結果。方法接收個參數,第一個參數是狀態的回調函數,第二個參數可選是狀態的回調函數。簡單來講,就是能把原來的回調寫法分離出來,在異步操作執行完后,用鏈式調用的方式執行回調函數。 在ECMAScript 6標準中,Promise被正式列為規范,Promise,字面意思就是許諾,承諾,嘿,聽著是不是很浪漫的說?我們來探究一下這個浪...
閱讀 2975·2021-09-22 15:18
閱讀 3397·2019-08-30 15:54
閱讀 3276·2019-08-30 15:53
閱讀 593·2019-08-30 14:12
閱讀 819·2019-08-29 17:01
閱讀 2204·2019-08-29 14:04
閱讀 1390·2019-08-29 13:09
閱讀 867·2019-08-26 17:40