Promise是異步編程的一種解決方案,所謂的Promise,簡單來講就是一個容器,裏面保存着將來纔會結束的事件的結果。編程
Promise對象有如下兩個特色:json
1.對象的狀態不受外界影響。Promise對象表明一種異步操做,有三個狀態 PENDING(進行中)、FULFILLED(已成功)、REJECTED(已失敗)
只有異步操做的結果能夠改變狀態
,其餘的任何操做都不能改變狀態。數組
2.一旦狀態改變了,就不會再變了,任什麼時候候均可以獲得這個結果。promise
MyPromise
接收一個函數executor
,executor
有兩個參數resolve
方法和reject
方法resolve
將PENDING
改變爲FULFILLED
reject
將PENDING
改變爲REJECTED
promise
變爲FULFILLED
狀態後具備一個惟一的value
promise
變爲REJECTED
狀態後具備一個惟一的reason
只要這兩種狀況發生了,狀態就不會再改變了,而且會一直保持這個結果。這與事件監聽不一樣,事件的特色是,不一樣時間監聽,獲得的結果都是不一樣的。服務器
Promise
規範有不少,如Promise/A
,Promise/B
,Promise/D
以及 Promise/A
的升級版 Promise/A+
。ES6中採用了 Promise/A+
規範babel
promise
採用了觀察者模式 用特定方式 註冊 對應 狀態 的事件處理函數markdown
Promise/A+並未規範race
、all
、catch
方法,這些是ES6
本身規範的。閉包
finally 是babel 轉es5
加的。異步
固然Promise也有一些缺點:async
首先沒法取消Promise
,一旦建立他就會當即執行,中途沒法取消,除非包裝一層,返回一個閉包異步操做結果要改變狀態的時候根據取消的參數決定是否改變
其次,若是還不設置回調函
數,Promise內部跑出的錯誤,不會反映到外部。
最後,當處於Pending狀態
時,沒法得知目前進展到哪個階段了。
Promise對象是一個構造函數
,用來生成Promise實例的。
const promise = new Promise(function(resolve, reject){
...
if(/* 異步操做成功 */){
resolve(value)
} else {
reject(error);
}
})
複製代碼
Promise構造函數接受一個函數做爲參數,該函數的兩個參數分別是resolve和reject
。他們是兩個函數,由js引擎提供,不須要本身部署
。
1.resolve函數的做用是: 將Promise對象的狀態從將PENDING
改變爲FULFILLED
,在異步操做成功時
調用,並將一步操做的結果,做爲參數傳遞出去。
2 reject函數的做用是:
將Promise對象的狀態從「未成功」變成「失敗」(Pending->REJECTED),在異步操做失敗時候調用
,並將異步操做報出的錯誤,做爲參數傳遞出去。
Promise實例生成之後,能夠用then方法
分別指定FULFILLED狀態和REJECTED狀態的回調函數
。
promise.then(function(value){//success}, function(err){// failure})
複製代碼
then方法能夠接受兩個回調函數做爲參數。第一個回調函數是Promise對象的狀態變爲FULFILLED時調用
,第二個回調函數是Promise對象的狀態變成Rejected時調用
。其中,第二個函數是可選的
,不必定要提供。這兩個函數都接受Promise對象傳出的值
做爲參數。
這裏咱們就能夠用Promise來異步加載圖片:
function loadImageAsync(url) {
return new Promise(function(resolve, reject) {
const image = new Image();
image.onload = function() {
resolve(image);
}
imgage.onerror = function() {
reject(new Error())
}
image.src = url;
})
}
複製代碼
then方法返回的是一個新的Promise實例
(注意,不是原來那個Promise實例)。所以能夠採用鏈式寫法
,即then方法後面再調用另外一個then方法。
getJSON("/posts.json").then(function(json) {
return json.post;
}).then(function(post) {
// ...
});
複製代碼
這一段代碼中使用了then方法,依次指定了兩個回調函數。第一個回調函數完成之後,會將返回結果做爲參數,傳入第二個回調函數中。
Promise.prototype.catch方法是.then(null, rejection)的別名,用於指定發生錯誤時的回調函數
。
getJSON("/posts.json").then(function(posts) {
// ...
}).catch(function(error) {
// 處理 getJSON 和 前一個回調函數運行時發生的錯誤
console.log('發生錯誤!', error);
});
複製代碼
Promise.all方法用於將多個Promise實例
,包裝成一個新的Promise實例。
var p = Promise.all([p1, p2, p3]);
當p1p2p3的狀態都是FULFILLED的時候,p的狀態纔是FULFILLED,不然只要有一個是rejected
,那麼p的狀態就時rejected
。 裏面是空數組的時候,promise.all當即決議爲成功
Promise.race()方法一樣是將多個Promise實例,包裝成一個新的Promise實例,用法和all是同樣的。
它與all的不一樣在於:race是只要有一個狀態改變
了,p的狀態就會改變,而且變成和第一個狀態改變以後
的同樣的狀態。
有時候須要將現有的對象轉爲Promise對象
,Promise.resolve方法就起到這個做用。
Promise.resolve('foo')
// 等價於new Promise(resolve => resolve('foo'))
複製代碼
Promise.resolve方法的參數分爲四種狀況:
1.參數是一個Promise實例
若是參數是Promise實例,那麼Promise.resolve將不作任何修改、原封不動地返 回這個實例。
let poruomiesi = new Promise(resolve => {
resolve('耶!');
});
//直接返回傳遞進去的promise
let p = Promise.resolve(poruomiesi);//p === poruomiesi
p.then(data => void console.log(data));
複製代碼
2.參數是一個thenable對象
thenable對象指的是具備then方法的對象
,好比下面這個對象。 Promise.resolve方法會將這個對象轉爲Promise對象,而後當即執行thenable對象的then方法。
let obj = {
then(cb) {
console.log('執行then');
cb('cb');
}
}
// 當即執行then方法
Promise.resolve(obj).then(data => {
console.log(data);
})
複製代碼
3.參數不是具備then方法的對象,或根本就不是對象
若是參數是一個原始值
,或者是一個不具備then方法的對象
,則Promise.resolve 方法返回一個新的Promise對象,狀態爲FULFILLED
let p1 = new Promise(resolve => {
resolve('成功!');
});
let p2 = Promise.resolve('成功!');
//注:p1和p2是等價的
複製代碼
四、Promise.resolve方法容許調用時不帶參數,直接返回一個Resolved狀態的Promise對象。 因此,若是但願獲得一個Promise對象,比較方便的方法就是直接調用Promise.resolve方法
Promise.reject(reason)方法也會返回一個新的Promise實例,該實例的狀態爲rejected。它的參數用法與Promise.resolve方法徹底同樣。
1.done()方法
Promise對象的回調鏈,無論以then方法或catch方法結尾
,要是最後一個方法拋出錯誤
,都有可能沒法捕捉到(由於Promise內部的錯誤不會冒泡到全局
)。 所以,咱們能夠提供一個done方法,老是處於回調鏈的尾端
,保證拋出任何可能出現的錯誤
。
asyncFunc().then(f1).catch(f2).then(f3).done(f4);
done 源碼:
Promise.prototype.done = function(onFulilled, onRejexted) {
this.then(onFulfilled, onRejected).catch(function(reason){setTimeout(()=>{
throw reason
}, 0);})
}
複製代碼
從上面代碼可見,done
方法的使用,能夠像then
方法那樣用,提供fulfilled
和rejected
狀態的回調函數,也能夠不提供任何參數。但無論怎樣,done
都會捕捉到任何可能出現的錯誤,並向全局拋出.
2.finally()方法
finally方法用於指定無論Promise對象最後狀態
如何,都會執行的操做。它與done方法最大的區別就在於,它接受一個普通的回調函數
做爲參數,該參數無論怎樣都必須執行。 舉個例子 服務器處理Promise 請求, 而後使用過finally 方法關掉服務器。
server.listen(0).then(function(){// run test}).finally(server.stop);
複製代碼
源碼:
Promise.prototype.finally = function(callback) {
let P = this.constructor
return this.then(
value => P.resolve(callback()).then(()=> value),
reason => P.resolve(callback()).then(()=> reason)
)
}
複製代碼
const PENDING = 'pending';
const FULFULLED= 'fulffilled';
const REJECTED = 'rejected';
function Promise(executor) {
this.state = PENDING;
this.value = null;
this.reason = null;
try {
executor(resolve, reject);
} catch (reason) {
reject(reason)
}
const resolve = (value)=> {
if(this.state === PENDING) {
this.state = FULFILLED;
this.value;
}
}
const reject = (reason) => {
if(this.state === PENDING) {
this.state = REJECTED;
this.reason = reason;
}
}
}
複製代碼