ES6 异步编程之二:Promise
異步回調的泥潭
異步回調是最直接的異步結果處理模式,將一個回調函數callback扔進異步處理函數中,當異步處理獲得結果之后再調用這個回調函數就可以繼續之后的處理,但是如果這個callback又是一個異步調用呢?眾所周知的,在JavaScript中異步回調的層層嵌套幾乎是反人性的,代碼編寫、修改和閱讀對我等有代碼潔癖的人而言是一種煎熬,這便是異步回調泥潭了。
今天對于處理異步調用已經有了很多成熟的方案,在我看來這些方案都無外乎在解決一個問題:“如何能看似順序地傳遞異步調用的結果?”,本文要說的Promise就是ES6原生提供的一個解決方案。
在對Promise進行敘述之前,依舊引用阮大的《ECMAScript 6入門》一書中的Promise章節便于大家更嚴謹和全面的學習和參考。
Promise
承諾,即對未來的許諾,如果諾言實現,然后(then)就如何如何……Promise極其生動的講述了一個言出必行的故事。
new Promise(function(resolve, reject){//開始實現承諾........if(承諾兌現時) {resolve(dollars); //兌現承諾的結果是得到'一大筆美金'} else {reject('絕交'); //沒兌現承諾就絕交}}).then(function(dollars){ //然后有錢了,買房買車娶妻生子let d1 = buyHouse(dollars); //把每次消費剩余的錢傳給下一個函數let d2 = buyCar(d1);let d3 = marry(d2);makeBaby(d3);}).catch(function(result){//然后如果絕交了,還是繼續吃土//繼續吃土});console.log('故事開始....');看過上面的這個俗不可耐的故事之后需要理解幾件事情:
言出必行:一個Promise構造出來之后,構造時傳入的異步函數就立即執行;*
注:因大凡使用promise都是在異步調用場景,下文所說的異步函數都是指構造promise時傳入的函數*
Promise實例內部維護了一個狀態機,狀態變化只可能是pending到resolved或者pending到rejected;
執行resolve:pending變化到resolved
執行reject:pending變化到rejected
拋出錯誤:pending變化到rejected
then的第一個回調函數只會在發生了resolve之后執行,本質上是在Promise到達resolved狀態執行;
then的第二個回調函數或者catch的回調函數會在發生reject之后或者異步函數執行拋出錯誤時執行,本質上是在promise到達rejected狀態時執行;
異步函數執行得到結果可以通過resolve或者reject將結果傳出;
調用resolve傳入的值會作為then第一個回調函數的入參
調用reject傳入的值作為then第二個回調函數或者catch的回調函數的入參
如果異步函數拋出了異常,異常會作為then第二個回調函數或者catch的回調函數的入參
'故事開始....'會先輸出,而不是等到then的回調函數執行完畢才輸出,說明傳入then的回調函數是異步執行,同理catch也是一樣;
異步函數調用鏈
then和catch都是Promise的實例方法,都返回一個新的Promise,因此可以輕而易舉地實現鏈式編程,比如上面的例子中“把每次消費剩余的錢”傳給下一個函數可以改寫成這樣:
....//前面省略.then(function(dollars){ return buyHouse(dollars);}).then(function(d1){return buyCar(d1);}).then(function(d2){return marry(d2);}).then(function(d3){return makeBaby(d3);}).catch(function(result){//繼續吃土});看到這里你可能認為前一個then回調函數的返回值是后一個then的回調函數的入參,但這是不準確的,因為當then回調函數返回的是個Promise對象時,這個Promise對象到終態時后一個then才會執行,并且該Promise對象執行resolve時的入參才是后一個then的回調函數入參;
此時有必要對Promise的一個類方法resolve做以下說明,它的特性兩句話:
如果傳入的是個Promise對象,則直接返回這個Promise;
如果是其他任何一個值(包括Error對象和undefined)則直接轉換為一個resolved狀態的Promise對象;
比如說下面的代碼:
//以下的p1和p2邏輯上等同let p1 = Promise.resolve(1);let p2 = new Promise(function(resolve, reject) {resolve(1);});//以下的p3和p4等同let p3 = new Promise(function(r, j) {});let p4 = Promise.resolve(p3);console.log(p3 == p4); //trueconsole.log(p3 === p4); //true//以下三者邏輯上等同Promise.resolve().then(function(dollars) {return 1 + 1;}).then(function(v) {console.log(v);});Promise.resolve().then(function(dollars) {return new Promise(function(r, j) { r(1 + 1) });}).then(function(v) {console.log(v);});Promise.resolve().then(function(dollars) {return Promise.resolve(1 + 1);}).then(function(v) {console.log(v);});我們可以利用Promise異步執行結果傳出的機制和then的鏈式調用,將層層嵌套的函數調用變為通過then順序連接的鏈式調用
從寫法和形式上看是不是人性很多呢?
通過Promise實現的鏈式異步函數調用,以斐波那契數列舉例如下:
//一個異步的斐波那契計算 function fibonacci(v) { return new Promise(function(resolve, reject) { //每一個異步調用都返回了一個PromisesetTimeout(function() {console.log(`${v.a}`);[v.a, v.b] = [v.b, v.a + v.b];resolve(v);}, 500);}); }//以下兩者邏輯等同,每個then都等待上一個promise的結果形成一條鏈。// fibonacci({ a: 0, b: 1 }) // .then(fibonacci) // .then(fibonacci) // .then(fibonacci) // .then(fibonacci) // .then(fibonacci) // .then(fibonacci);Promise.resolve().then(() => fibonacci({ a: 0, b: 1 })).then(fibonacci).then(fibonacci).then(fibonacci).then(fibonacci).then(fibonacci).then(fibonacci);總結
以上是生活随笔為你收集整理的ES6 异步编程之二:Promise的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 【知云】第十二期:处于风口浪尖上的直播视
- 下一篇: 如何让其他机器访问你的oracle数据库