一、promise描述
promise是javascript中標準的內置物件,用于表示一個異步操作的最終狀態(是失敗還是成功完成)及其結果值,它讓你能夠把異步操作最終成功或者失敗的原因和回應的處理程式相關聯,也就是說通過promise你可以自定義異步操作結束后該做什么,這樣的話異步方法就和同步方法很類似,也有回傳值,只不過這個回傳值不是立即回傳最終的值,而是回傳一個promise,當promise的狀態發生改變時,就會觸發相應的處理程式,
一個promise無論什么時候都必然處于以下幾種狀態:1、待定(pending) 2、已兌現(fulfilled) 3、已拒絕(rejected)
promise創建之初是待定狀態,它既咩有被兌現也咩有被拒絕,它用于包裝還沒有被添加promise支持的函式,包裝后的函式變成異步操作函式,當操作結束時,會有兩個兩個分支,一個是已兌現,一個是已拒絕,如下圖,已兌現狀態的promise會呼叫后續的then方法,而已拒絕狀態的promise既可以呼叫then方法,也可以被catch方法捕捉到,為什么then方法可以捕捉兩個狀態的promise呢?這里先不講,下面再詳細介紹,

二、promise 的流程走向
上圖是promise的流程圖,從左到右可以得知一個promise在待定狀態時通過兩個分支走向分別進入then方法或者catch方法,這兩個c方法都會回傳promise,如果這個promise也被敲定了,而且其后也有then方法或者catch方法,則又會進入后續的then和catch中,直至咪有,
也就是說,promise和then或者catch可以形成一個異步操作的鏈式結構,結合js的事件回圈機制,這使得js的優勢更加明顯,發展至今仍能在瀏覽器上頻繁看到它的身影,
我們要注意流程的特點:
1、promise是物件,它的轉移成功不取決于回傳值,而取決于狀態的敲定(是成功還是失敗)
2、then和catch是函式,只要有promise狀態發生了改變,該promise對應后面的then或者catch方法就會被呼叫,而這兩個方法本身會回傳一個promise,這個回傳的promise又會影響這兩個方法后面的then或者catch方法,兩個方法的回傳值一定是promise,
三、promise的創建
Promise 物件是由關鍵字 new 及其建構式來創建的,該建構式會把一個叫做“處理器函式”(executor function)的函式作為它的引數,這個“處理器函式”接受兩個函式——resolve 和 reject ——作為其引數,當異步任務順利完成且回傳結果值時,會呼叫 resolve 函式;而當異步任務失敗且回傳失敗原因(通常是一個錯誤物件)時,會呼叫reject 函式,示例如下:
let myFirstPromise = new Promise(function(resolve, reject){ //當異步代碼執行成功時,我們才會呼叫resolve(...), 當異步代碼失敗時就會呼叫reject(...) //在本例中,我們使用setTimeout(...)來模擬異步代碼,實際編碼時可能是XHR請求或是HTML5的一些API方法. setTimeout(function(){ resolve("成功!"); //代碼正常執行! }, 250); }); myFirstPromise.then(function(successMessage){ //successMessage的值是上面呼叫resolve(...)方法傳入的值. //successMessage引數不一定非要是字串型別,這里只是舉個例子 console.log("Yay! " + successMessage); });
四、promise的優勢
在promise未出現時,如果我們呼叫異步代碼塊的話是沒有辦法保持順序的,而如果又出現了異步代碼結果之間按序的需求,該如何實作呢?
以前的話通常都會將異步代碼一層一層地內嵌來實作異步代碼按序實作,但是這就造成了代碼維護困難,開發難度增大
doSomething(function(result) { doSomethingElse(result, function(newResult) { doThirdThing(newResult, function(finalResult) { console.log('Got the final result: ' + finalResult); }, failureCallback); }, failureCallback); }, failureCallback);
這就是經典的回呼地獄,而如果使用了前面介紹的promise,那么代碼就變成了容易維護的鏈式結構
五、then方法回傳的promise型別
當一個 Promise 完成(fulfilled)或者失敗(rejected)時,回傳函式將被異步呼叫(由當前的執行緒回圈來調度完成),具體的回傳值依據以下規則回傳,如果 then 中的回呼函式:
- 回傳了一個值,那么
then回傳的 Promise 將會成為接受狀態,并且將回傳的值作為接受狀態的回呼函式的引數值, - 沒有回傳任何值,那么
then回傳的 Promise 將會成為接受狀態,并且該接受狀態的回呼函式的引數值為undefined, - 拋出一個錯誤,那么
then回傳的 Promise 將會成為拒絕狀態,并且將拋出的錯誤作為拒絕狀態的回呼函式的引數值, - 回傳一個已經是接受狀態的 Promise,那么
then回傳的 Promise 也會成為接受狀態,并且將那個 Promise 的接受狀態的回呼函式的引數值作為該被回傳的Promise的接受狀態回呼函式的引數值, - 回傳一個已經是拒絕狀態的 Promise,那么
then回傳的 Promise 也會成為拒絕狀態,并且將那個 Promise 的拒絕狀態的回呼函式的引數值作為該被回傳的Promise的拒絕狀態回呼函式的引數值, - 回傳一個未定狀態(
pending)的 Promise,那么then回傳 Promise 的狀態也是未定的,并且它的終態與那個 Promise 的終態相同;同時,它變為終態時呼叫的回呼函式引數與那個 Promise 變為終態時的回呼函式的引數是相同的,
六、catch捕捉的錯誤
catch能捕捉promise組合中出現的錯誤,但是有兩種錯誤不能捕捉:
1、已決議的錯誤不能捕捉
//創建一個新的 Promise ,且已決議 var p1 = Promise.resolve("calling next"); var p2 = p1.catch(function (reason) { //這個方法永遠不會呼叫 console.log("catch p1!"); console.log(reason); }); p2.then(function (value) { console.log("next promise's onFulfilled"); /* next promise's onFulfilled */ console.log(value); /* calling next */ }, function (reason) { console.log("next promise's onRejected"); console.log(reason); });
2、異步函式中拋出的錯誤不能捕捉
需要注意的是,作者親手實踐發現:promise包裹的異步函式執行成功后必須顯式地呼叫resolve和reject方法才能觸發后續then和catch方法,如果promise方法包裹的不是異步函式,是普通的同步函式,如果該同步代碼運行出錯,即便沒有呼叫reject方法時,后面的catch方法仍然能夠捕捉到這個錯誤,但如果同步代碼沒有出錯,沒有顯式呼叫resolve方法轉移的話,后續的then方法不會觸發,
七、高級示例
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Document</title> <style> *{ margin: 10px; } html{ width: 100%; height: 100%; } body{ width: 100%; height: 100%; display:flex; align-items: center; justify-content: center; } div.displaydatabox{ width: 300px; height: 300px; border-radius: 50px; text-align: center; line-height: 300px; box-shadow: 0 0 10px 2px black; } div.button{ width: 100px; height: 50px; border-radius: 21px; border: 2px solid orange; line-height: 50px; text-align: center; cursor: pointer; } </style> </head> <body> <div class="button">創建</div> <div class="button">輸入文字</div> <div class="button">消失</div> <script lang="javascript"> let buttonlist=document.querySelectorAll("div.button"); let body=document.querySelector("body"); buttonlist[0].onclick=function() { let div=document.createElement("div"); div.className="displaydatabox"; body.appendChild(div); } buttonlist[2].onclick=function() { let div=document.querySelector("div.displaydatabox"); body.removeChild(div); } buttonlist[1].onclick=function(e) { let p1=new Promise((resolve,reject)=> { setTimeout(()=>{//用setTimeout函式模擬異步函式 let div=document.querySelector("div.displaydatabox"); div.textContent="這是promise實驗"; //reject(1); resolve(1);//呼叫resolve將呼叫第一個then },2000); }).then(function(resolve){ return new Promise((resolve,reject)=>{ console.log("這是狀態咩有敲定的promise,所以不會呼叫后面所有的then方法");
//resolve(1)//沒有呼叫resolve或者reject,所以狀態未敲定,如果呼叫,將輸出1和finally it has been called!! }) .then(function(e){ console.log(e); }); }).catch(function(e) { console.log(e+" 沒有塊可以輸入!!"); }).then(()=> { console.log("finally it has been called!!"); }) } </script> </body> </html>
轉載請註明出處,本文鏈接:https://www.uj5u.com/qiye/248937.html
標籤:JavaScript
上一篇:nvm-node版本管理工具
下一篇:一次Vue代碼bug定位
