轉載請註明出處:
Promise 物件是 JavaScript 的非同步操作解決方案,為非同步操作提供統一介面。它起到代理作用(proxy),充當非同步操作與回撥函數之間的中介,使得非同步操作具備同步操作的介面。Promise 可以讓非同步操作寫起來,就像在寫同步操作的流程,而不必一層層地巢狀回撥函數。
由於ajax非同步方式請求資料時,我們不能知道資料具體回來的事件,所以過去只能將一個callback函數傳遞給ajax封裝的方法,當ajax非同步請求完成時,執行callback函數。
promise物件接受resolve和reject兩個引數,當一個非同步動作發生時,promise物件會通過resolve完成對動作成功進行解析,reject會捕獲這個動作的異常。一個promise物件,通過new Promise().then()執行下一步驟操作。
axios is a promise based HTTP client for the browser and node.js。也就是說,使用axios發出請求,難免涉及promise
Promise建構函式的引數是一個函數,函數裡面的程式碼是非同步的,即Promise裡面的操作,和Promise()外面的操作時非同步"同時"進行的。Promise中的函數的第一個引數是回撥函數,resolve用來觸發then裡面的程式碼,第二個引數是回撥函數,reject用來觸發catch中的程式碼,throw new Error();也可以觸發catch,
<script> new Promise((resolve, reject) =>{ setTimeout(() =>{ //成功的時候呼叫resolve resolve('成功data') //失敗的時候呼叫reject reject('error message') }, 1000) }).then((data) =>{ //處理成功後的邏輯 console.log(data);//這個data 是接收的resolve引數-- }).catch((err) =>{ console.log(err); }) </script>
傳統的鏈式呼叫程式設計如下:
// 傳統寫法 step1(function (value1) { step2(value1, function(value2) { step3(value2, function(value3) { step4(value3, function(value4) { // ... }); }); }); });
使用 Promise 之後,可以簡化為如下:
// Promise 的寫法 (new Promise(step1)) .then(step2) .then(step3) .then(step4);
從上面程式碼可以看到,採用 Promises 以後,程式流程變得非常清楚,十分易讀。注意,為了便於理解,上面程式碼的Promise
範例的生成格式,做了簡化
Promise 物件通過自身的狀態,來控制非同步操作。Promise 範例具有三種狀態。
非同步操作未完成(pending)
非同步操作成功(fulfilled)
非同步操作失敗(rejected)
上面三種狀態裡面,fulfilled和rejected合在一起稱為resolved(已定型)。
這三種的狀態的變化途徑只有兩種。
從「未完成」到「成功」
從「未完成」到「失敗」
一旦狀態發生變化,就凝固了,不會再有新的狀態變化。這也是 Promise 這個名字的由來,它的英語意思是「承諾」,一旦承諾成效,就不得再改變了。這也意味著,Promise 範例的狀態變化只可能發生一次。
因此,Promise 的最終結果只有兩種。
非同步操作成功,Promise 範例傳回一個值(value),狀態變為fulfilled。
非同步操作失敗,Promise 範例丟擲一個錯誤(error),狀態變為rejected。
JavaScript 提供原生的Promise
建構函式,用來生成 Promise 範例。
var promise = new Promise(function (resolve, reject) { // ... if (/* 非同步操作成功 */){ resolve(value); } else { /* 非同步操作失敗 */ reject(new Error()); } });
上面程式碼中,Promise建構函式接受一個函數作為引數,該函數的兩個引數分別是resolve和reject。它們是兩個函數,由 JavaScript 引擎提供,不用自己實現。
resolve函數的作用是,將Promise範例的狀態從「未完成」變為「成功」(即從pending變為fulfilled),在非同步操作成功時呼叫,並將非同步操作的結果,作為引數傳遞出去。reject函數的作用是,將Promise範例的狀態從「未完成」變為「失敗」(即從pending變為rejected),在非同步操作失敗時呼叫,並將非同步操作報出的錯誤,作為引數傳遞出去。
下面是一個例子。
function timeout(ms) { return new Promise((resolve, reject) => { setTimeout(resolve, ms, 'done'); }); } timeout(100)
上面程式碼中,timeout(100)
返回一個 Promise 範例。100毫秒以後,該範例的狀態會變為fulfilled
。
then
方法,用來新增回撥函數。
then
方法可以接受兩個回撥函數,第一個是非同步操作成功時(變為fulfilled
狀態)時的回撥函數,第二個是非同步操作失敗(變為rejected
)時的回撥函數(該引數可以省略)。一旦狀態改變,就呼叫相應的回撥函數。
var p1 = new Promise(function (resolve, reject) { resolve('成功'); }); p1.then(console.log, console.error); // "成功" var p2 = new Promise(function (resolve, reject) { reject(new Error('失敗')); }); p2.then(console.log, console.error); // Error: 失敗
上面程式碼中,p1和p2都是Promise 範例,它們的then方法系結兩個回撥函數:成功時的回撥函數console.log,失敗時的回撥函數console.error(可以省略)。p1的狀態變為成功,p2的狀態變為失敗,對應的回撥函數會收到非同步操作傳回的值,然後在控制檯輸出。
then方法可以鏈式使用。
p1.then(step1)
.then(step2)
.then(step3)
.then(
console.log,
console.error
);
上面程式碼中,p1後面有四個then,意味依次有四個回撥函數。只要前一步的狀態變為fulfilled,就會依次執行緊跟在後面的回撥函數。
最後一個then方法,回撥函數是console.log和console.error,用法上有一點重要的區別。console.log只顯示step3的返回值,而console.error可以顯示p1、step1、step2、step3之中任意一個發生的錯誤。舉例來說,如果step1的狀態變為rejected,那麼step2和step3都不會執行了(因為它們是resolved的回撥函數)。Promise 開始尋找,接下來第一個為rejected的回撥函數,在上面程式碼中是console.error。這就是說,Promise 物件的報錯具有傳遞性。
Promise 的優點在於,讓回撥函數變成了規範的鏈式寫法,程式流程可以看得很清楚。它有一整套介面,可以實現許多強大的功能,比如同時執行多個非同步操作,等到它們的狀態都改變以後,再執行一個回撥函數;再比如,為多個回撥函數中丟擲的錯誤,統一指定處理方法等等。
而且,Promise 還有一個傳統寫法沒有的好處:它的狀態一旦改變,無論何時查詢,都能得到這個狀態。這意味著,無論何時為 Promise 範例新增回撥函數,該函數都能正確執行。所以,你不用擔心是否錯過了某個事件或訊號。如果是傳統寫法,通過監聽事件來執行回撥函數,一旦錯過了事件,再新增回撥函數是不會執行的。
Promise 的缺點是,編寫的難度比傳統寫法高,而且閱讀程式碼也不是一眼可以看懂。你只會看到一堆then,必須自己在then的回撥函數裡面理清邏輯。
https://es6.ruanyifeng.com/#docs/promise