Promise 是異步編程的一種解決方案,比傳統的解決方案——回調函數和事件——更合理和更強大。它由社區最先提出和實現,ES6 將其寫進了語言標準,統一了用法,原生提供了Promise
對象。git
所謂Promise
,簡單說就是一個容器,裏面保存着某個將來纔會結束的事件(一般是一個異步操做)的結果。從語法上說,Promise 是一個對象,從它能夠獲取異步操做的消息。Promise 提供統一的 API,各類異步操做均可以用一樣的方法進行處理。github
Promise
對象有如下兩個特色。編程
(1)對象的狀態不受外界影響。Promise
對象表明一個異步操做,有三種狀態:pending
(進行中)、fulfilled
(已成功)和rejected
(已失敗)。只有異步操做的結果,能夠決定當前是哪種狀態,任何其餘操做都沒法改變這個狀態。這也是Promise
這個名字的由來,它的英語意思就是「承諾」,表示其餘手段沒法改變。數組
(2)一旦狀態改變,就不會再變,任什麼時候候均可以獲得這個結果。Promise
對象的狀態改變,只有兩種可能:從pending
變爲fulfilled
和從pending
變爲rejected
。只要這兩種狀況發生,狀態就凝固了,不會再變了,會一直保持這個結果,這時就稱爲 resolved(已定型)。若是改變已經發生了,你再對Promise
對象添加回調函數,也會當即獲得這個結果。這與事件(Event)徹底不一樣,事件的特色是,若是你錯過了它,再去監聽,是得不到結果的。promise
有了Promise
對象,就能夠將異步操做以同步操做的流程表達出來,避免了層層嵌套的回調函數。此外,Promise
對象提供統一的接口,使得控制異步操做更加容易。bash
Promise
也有一些缺點。首先,沒法取消Promise
,一旦新建它就會當即執行,沒法中途取消。其次,若是不設置回調函數,Promise
內部拋出的錯誤,不會反應到外部。第三,當處於pending
狀態時,沒法得知目前進展到哪個階段(剛剛開始仍是即將完成)。框架
Promise
構造函數接受一個函數做爲參數,該函數的兩個參數分別是resolve
和reject
。它們是兩個函數,由 JavaScript 引擎提供,不用本身部署。異步
resolve
函數的做用是,將Promise
對象的狀態從「未完成」變爲「成功」(即從 pending 變爲 resolved),在異步操做成功時調用,並將異步操做的結果,做爲參數傳遞出去;reject
函數的做用是,將Promise
對象的狀態從「未完成」變爲「失敗」(即從 pending 變爲 rejected),在異步操做失敗時調用,並將異步操做報出的錯誤,做爲參數傳遞出去。異步編程
var p=new Promise(function(resolve,rejcet){
setTimeout(function(){
if(true){
//異步操做成功
resolve('success');
}else{
//異步操做失敗
rejcet('failure');
}
},1000);
});
p.then(function(value){
//成功的回調
console.log(value);
},function(error){
//失敗的回調
console.log(error);
});複製代碼
從demo中能夠看到,在ES6中Promise
對象是一個構造函數,用來生成Promise
實例。而且Promise
實例生成之後,能夠用then
方法分別指定resolved
狀態和rejected
狀態的回調函數。函數
function Promise(callback) {
var self = this //promise實例
self.status = 'PENDING' // Promise當前的狀態
self.data = undefined // Promise的值
self.onResolvedCallback = [] // Promise resolve時的回調函數集
self.onRejectedCallback = [] // Promise reject時的回調函數集
callback(resolve, reject) // 執行executor並傳入相應的參數
function resolve(value){
}
function reject(error){
}
}
// 添加咱們的then方法
Promise.prototype.then=function(){
}複製代碼
咱們先建立一個Promise構造函數,並傳入一個回調函數callback,callback裏面傳入兩個函數做爲參數,一個是resove,一個是reject。並在Promise的原型上加入咱們的then方法。框架搭好了,接下來咱們來一點點的完善框架裏面的內容,能夠這麼說,把resolve,reject和then補充完,基本能夠說就是把Promise完成了。
function Promise(callback) {
var self = this
self.status = 'PENDING' // Promise當前的狀態
self.data = undefined // Promise的值
self.onResolvedCallback = [] // Promise resolve時的回調函數集
self.onRejectedCallback = [] // Promise reject時的回調函數集
callback(resolve, reject) // 執行executor並傳入相應的參數
function resolve(value){
if(self.status=='PENDING'){
self.status=='FULFILLED';
self.data=value;
// 依次執行成功以後的函數棧
for(var i = 0; i < self.onResolvedCallback.length; i++) {
self.onResolvedCallback[i](value)
}
}
}
function rejecte(error){
if (self.status === 'PENDING') {
self.status = 'REJECTED'
self.data = error;
// 依次執行失敗以後的函數棧
for(var i = 0; i < self.onRejectedCallback.length; i++) {
self.onRejectedCallback[i](error)
}
}
}
}複製代碼
then方法是Promise的核心,所以這裏會花比較大的篇幅去介紹then:
一個promise的then接受兩個參數:
promise.then(onFulfilled, onRejected)複製代碼
onFulfilled
和 onRejected
都是可選參數。
onFulfilled
不是函數,其必須被忽略onRejected
不是函數,其必須被忽略onFulfilled 特性
若是 onFulfilled
是函數:
promise
執行結束後其必須被調用,其第一個參數爲 promise
的終值,也就是resolve傳過來的值promise
執行結束前其不可被調用onRejected 特性
若是 onRejected
是函數:
promise
被拒絕執行後其必須被調用,其第一個參數爲 promise
的據因,也就是reject傳過來的值promise
被拒絕執行前其不可被調用調用時機
onFulfilled
和 onRejected
只有在執行環境堆棧僅包含平臺代碼時纔可被調用(平臺代碼指引擎、環境以及 promise 的實施代碼)
調用要求
onFulfilled
和 onRejected
必須被做爲函數調用(即沒有 this
值,在嚴格模式(strict) 中,函數 this
的值爲 undefined
;在非嚴格模式中其爲全局對象。)
屢次調用
then
方法能夠被同一個 promise
調用屢次
promise
成功執行時,全部 onFulfilled
需按照其註冊順序依次回調promise
被拒絕執行時,全部的 onRejected
需按照其註冊順序依次回調then
方法必須返回一個 promise
對象
promise2 = promise1.then(onFulfilled, onRejected);複製代碼
onFulfilled
或者 onRejected
返回一個值 x
,則運行下面的 Promise 解決過程:[[Resolve]](promise2, x)
onFulfilled
或者 onRejected
拋出一個異常 e
,則 promise2
必須拒絕執行,並返回拒因 e
onFulfilled
不是函數且 promise1
成功執行, promise2
必須成功執行並返回相同的值onRejected
不是函數且 promise1
拒絕執行, promise2
必須拒絕執行並返回相同的據因不論 promise1
被 reject 仍是被 resolve 時 promise2
都會被 resolve,只有出現異常時纔會被 rejected。
每一個Promise對象均可以在其上屢次調用then方法,而每次調用then返回的Promise的狀態取決於那一次調用then時傳入參數的返回值,因此then不能返回this,由於then每次返回的Promise的結果都有可能不一樣。
Promise.prototype.then = function(onResolved, onRejected) {
var self = this
var promise2
// 根據標準,若是then的參數不是function,則咱們須要忽略它,此處以以下方式處理
onResolved = typeof onResolved === 'function' ? onResolved : function(value) {}
onRejected = typeof onRejected === 'function' ? onRejected : function(reason) {}
if (self.status === 'resolved') {
// 若是promise1(此處即爲this/self)的狀態已經肯定而且是resolved,咱們調用onResolved
// 由於考慮到有可能throw,因此咱們將其包在try/catch塊裏
return promise2 = new Promise(function(resolve, reject) {
try {
var x = onResolved(self.data)
if (x instanceof Promise) { // 若是onResolved的返回值是一個Promise對象,直接取它的結果作爲promise2的結果
x.then(resolve, reject)
}
resolve(x) // 不然,以它的返回值作爲promise2的結果
} catch (e) {
reject(e) // 若是出錯,以捕獲到的錯誤作爲promise2的結果
}
})
}
// 此處與前一個if塊的邏輯幾乎相同,區別在於所調用的是onRejected函數,就再也不作過多解釋
if (self.status === 'rejected') {
return promise2 = new Promise(function(resolve, reject) {
try {
var x = onRejected(self.data)
if (x instanceof Promise) {
x.then(resolve, reject)
}
} catch (e) {
reject(e)
}
})
}
if (self.status === 'pending') {
// 若是當前的Promise還處於pending狀態,咱們並不能肯定調用onResolved仍是onRejected,
// 只能等到Promise的狀態肯定後,才能確實如何處理。
// 因此咱們須要把咱們的**兩種狀況**的處理邏輯作爲callback放入promise1(此處即this/self)的回調數組裏
// 邏輯自己跟第一個if塊內的幾乎一致,此處不作過多解釋
return promise2 = new Promise(function(resolve, reject) {
self.onResolvedCallback.push(function(value) {
try {
var x = onResolved(self.data)
if (x instanceof Promise) {
x.then(resolve, reject)
}
} catch (e) {
reject(e)
}
})
self.onRejectedCallback.push(function(reason) {
try {
var x = onRejected(self.data)
if (x instanceof Promise) {
x.then(resolve, reject)
}
} catch (e) {
reject(e)
}
})
})
}
}
// 爲了下文方便,咱們順便實現一個catch方法
Promise.prototype.catch = function(onRejected) {
return this.then(null, onRejected)
}複製代碼
最後附上完整的代碼:
Promise.prototype.then = function(onResolved, onRejected) {
var self = this
var promise2
// 根據標準,若是then的參數不是function,則咱們須要忽略它,此處以以下方式處理
onResolved = typeof onResolved === 'function' ? onResolved : function(value) {}
onRejected = typeof onRejected === 'function' ? onRejected : function(reason) {}
if (self.status === 'resolved') {
// 若是promise1(此處即爲this/self)的狀態已經肯定而且是resolved,咱們調用onResolved
// 由於考慮到有可能throw,因此咱們將其包在try/catch塊裏
return promise2 = new Promise(function(resolve, reject) {
try {
var x = onResolved(self.data)
if (x instanceof Promise) { // 若是onResolved的返回值是一個Promise對象,直接取它的結果作爲promise2的結果
x.then(resolve, reject)
}
resolve(x) // 不然,以它的返回值作爲promise2的結果
} catch (e) {
reject(e) // 若是出錯,以捕獲到的錯誤作爲promise2的結果
}
})
}
// 此處與前一個if塊的邏輯幾乎相同,區別在於所調用的是onRejected函數,就再也不作過多解釋
if (self.status === 'rejected') {
return promise2 = new Promise(function(resolve, reject) {
try {
var x = onRejected(self.data)
if (x instanceof Promise) {
x.then(resolve, reject)
}
} catch (e) {
reject(e)
}
})
}
if (self.status === 'pending') {
// 若是當前的Promise還處於pending狀態,咱們並不能肯定調用onResolved仍是onRejected,
// 只能等到Promise的狀態肯定後,才能確實如何處理。
// 因此咱們須要把咱們的**兩種狀況**的處理邏輯作爲callback放入promise1(此處即this/self)的回調數組裏
// 邏輯自己跟第一個if塊內的幾乎一致,此處不作過多解釋
return promise2 = new Promise(function(resolve, reject) {
self.onResolvedCallback.push(function(value) {
try {
var x = onResolved(self.data)
if (x instanceof Promise) {
x.then(resolve, reject)
}
} catch (e) {
reject(e)
}
})
self.onRejectedCallback.push(function(reason) {
try {
var x = onRejected(self.data)
if (x instanceof Promise) {
x.then(resolve, reject)
}
} catch (e) {
reject(e)
}
})
})
}
}
// 爲了下文方便,咱們順便實現一個catch方法
Promise.prototype.catch = function(onRejected) {
return this.then(null, onRejected)
}複製代碼