一、Promise 的含義 Promise 是非同步編程的一種解決方案,所謂Promise,簡單來說就是一個容器,裡面保存著一個非同步操作的結果。 Promise對象有以下兩個特點: 1、對象的狀態不受外界的影響。Promise對象代表一個非同步操作,有三種狀態:pending(進行中)、fulfille ...
一、Promise 的含義
Promise 是非同步編程的一種解決方案,所謂Promise,簡單來說就是一個容器,裡面保存著一個非同步操作的結果。
Promise對象有以下兩個特點:
1、對象的狀態不受外界的影響。Promise對象代表一個非同步操作,有三種狀態:pending(進行中)、fulfilled(已成功)、rejected(已失敗)。只有非同步操作的結果,可以決定當前是哪一種狀態,任何其他操作都無法改變這個狀態。
2、一旦狀態改變,就不會再變,任何時候都可以得到這個結果。Promise對象的狀態改變,只有兩種可能:從pending變為fulfilled和從pending變為rejected。只要這兩種情況發生,狀態就凝固了,不會再發生改變,會一直保持這個結果,這時就被稱為resolved(已定型)。如果改變已經發生了,你再對Promise對象添加回調函數,也會立即得到這個結果。這與事件(event)完全不同,事件的特點是,如果你錯過了它,再去監聽,是得不到結果的。
有了Promise對象,就可以將非同步操作以同步操作的流程表達出來,避免了層層嵌套的回調函數。此外,Promise對象提供統一的介面,使得控制非同步操作更加容易哦。
當然,Promise也是有有一些缺點的,首先,無法取消Promise,一旦新建他就會立即執行,無法中途取消。其次就是,如果不設置回調函數,Promise內部拋出的錯誤,就不會反應到外部。還有就是,當處於pending狀態時,無法得知目前進展到哪一個階段。
二、基本用法
在ES6中,Promise對象是一個構造函數,用來生成Promise實例。
Promise構造函數接受一個函數作為參數,該函數的兩個參數分別是resolve和reject。他們是兩個函數,由JavaScript引擎提供,不用自己部署。
resolve函數的作用是,將Promise對象的狀態從“未完成”變為“成功”(就是從pending狀態變為resolve狀態),在非同步操作時調用,並將非同步操作的結果,作為參數傳遞出去;reject函數的作用是將Promise對象的狀態從“未完成”變為“失敗”(就是從pending變為rejected),在非同步操作失敗時調用,並且將這個錯誤作為參數傳遞出去。
Promise實例生成之後,可以用then方法分別指定resolved和rejected的回調函數。
then方法可以接受兩個回調函數作為參數。第一個回調函數是Promise對象的狀態改變變為resolved時調用,第二個回調函數是Promise對象的狀態變為rejected時調用。並且,第二個回調函數是可選的,不一定提非要供。這兩個函數都接受Promise對象傳出的值作為參數。
上面的代碼中,timeout方法返回一個Promise實例,表示一段時間以後才會發生的結果。過了指定的時間(s參數)以後,Promise實例的狀態變為resolved,就會觸發then方法綁定的回調函數。
上面的代碼中,Promise新建後立即執行,所以首先輸出的是Promise。然後,then方法指定的回調函數,將在當前腳本所有同步任務執行完成才會執行,所以resolved最後輸出。
如果調用resolve函數和reject函數時帶有參數,那麼他們的參數會被傳遞給回調函數。reject函數的參數通常是error對象的實例,表示拋出的錯誤;resolve函數的參數除了正常的值以外,還可能是另一個Promise實例。
上面的代碼中,p1和p2都是Promise的實例,但是p2的resolve方法將p1作為參數,即一個非同步操作的結果是返回另一個非同步操作。
值得註意的是,這是的p1的狀態就會傳遞給p2,也就是說,p1的狀態決定了p2的狀態。如果p1的狀態是pending,那麼p2的回調函數就會等待p1的狀態改變;如果p1的狀態已經是resolved或者rejected,那麼p2的回調函數就會立刻執行。
三、Promise.prototype.then()
Promise實例具有then方法,也就是說,then方法是定義在原型對象Promise.prototype上的。它的作用是為Promise的實例添加狀態改變時的回調函數。在前面說過,then方法的第一個參數是resolved狀態的回調函數,第二個參數(可選)是rejected狀態的回調函數。
then方法返回的是一個新的Promise實例。因此可以採用鏈式寫法,即為then方法後面再調用一個then方法。
上面的代碼使用的是then方法,依次指定了兩個回調函數。第一個回調函數完成以後,會將返回結果作為參數,傳入第二個回調函數。
採用鏈式的then,可以指定一組按照次序調用的回調函數。這時,前一個回調函數,有可能返回的還是一個Promise對象(即有非同步操作),這時後一個回調函數,就會等這個Promise對象的狀態發生變化,才會被調用。
上面的代碼中,第一個then方法指定的回調函數,返回的是另一個Promise對象。這時,第二個then方法指定的回調函數,就會等待這個新的Promise對象狀態發生改變。如果變為resolved,就調用funcA,如果狀態變為rejected,就調用funcB。
四、Promise.resolve()
有時需要將對象轉為Promise對象,promise.resolve方法就起到這個作用。
上面 的代碼中將jQuery生成的deferred對象,轉為一個新的Promise對象。
Promise.resolve等價於以下寫法。
Promise.resolve方法的參數分成四種情況。
1、參數是一個Promise實例
如果參數是一個Promise實例,那麼Promise.resolve將不會做任何修改,原封不動的返回這個實例。
2、參數是一個thenable對象
thenable對象指的是具有then方法的對象。
Promise.resolve方法會將這個對象轉為Promise對象,然後就立即執行thenable對象的then方法。
上面代碼中,thenable對象的then方法執行後,對象p1的狀態就變為resolved,從而立即執行最後那個then方法指定的回調函數,輸出666.
3、參數不是具有then方法的對象,或根本就不是對象
如果參數是一個原始值,或者是一個不具有then方法的對象,則Promise.resolve方法返回一個新的Promise對象,狀態為resolved。
上面代碼生成一個新的Promise對象的實例p。由於字元串hello world不屬於非同步操作,返回Promise實例的狀態從一生成就是resolved,所以回調函數會立即執行。Promise.resolve方法的參數,會同時傳給回調函數。
4、不帶有任何參數
Promise.resolve方法允許調用時不帶任何參數,直接返回一個resolved狀態的Promise對象。如果所以想得到一個Promise對象,比較方便的方法就是直接調用Promise.resolve方法。
上面代碼的變數p就是一個Promise對象。
五、Promise.reject()
Promise.reject(reason)方法也會返回一個新的Promise實例,該實例的狀態為rejected。
上面代碼生成一個Promise對象的實例p,狀態為rejected,回調函數會立即執行。
要註意的是,Promise.reject()方法的參數,會原封不動的作為reject的理由,變成後續方法的參數。這與Promise.resolve方法不一致。
上面的代碼中,Promise.reject方法的參數是一個thenable對象,後面catch方法的參數不是reject拋出的‘出錯’這個字元串,而是thenable對象。