Promise 是異步編程的一種解決方案優於傳統的解決方案——回調函數和事件。 簡單說就是一個容器,裏面保存着某個將來纔會結束的事件(一般是一個異步操做)的結果。 Promise對象表明一個異步操做,有三種狀態:
pending
(進行中)、resolved
(已成功)和rejected
(已失敗)。es6
setTimeout(() => {
console.log('a');
}, 0);
let p = new Promise((resolve, reject) => {
console.log('b');
resolve();
});
p.then(() => {
console.log('d');
});
console.log('c');
// 控制檯輸出:
// 'b'
// 'c'
// 'd'
// 'a'
複製代碼
要理解該輸出順序首先應該瞭解js的執行任務隊列優先級(由高到低)編程
首先setTimeout
屬於宏任務扔進Macro-tasks隊列,新建實例Promise
時接受一個回調函數做爲參數,注意此時該回調函數屬於主線程會馬上執行,輸出'b'
緊接着執行resolve
也就意味着該promise
對象的狀態將從pending
更新爲resolved
,其掛載的回調函數也就是then裏面的參數函數並不會當即執行,由於它屬於微任務,因此丟進Micro-task隊列。接下來輸出'c'
,到目前爲止主線程任務已經結束,接着執行微任務輸出'd'
,最後執行宏任務輸出'a'
。數組
let p1 = new Promise(function (resolve, reject) {
resolve('p1');
});
let p2 = new Promise(function (resolve, reject) {
setTimeout(() => {
resolve('p2')
}, 100);
});
let p3 = new Promise(function (resolve, reject) {
setTimeout(() => {
reject('p3')
resolve('p3')
}, 100);
});
p1.then((value) => {
console.log(value);
})
p2.then((value) => {
console.log(value);
})
p3.then((value) => {
console.log('success', value);
}, (value) => {
console.log('error', value);
})
console.log('p1:', p1);
console.log('p2:', p2);
console.log('p3:', p3);
setTimeout(() => {
console.log('p1:', p1);
console.log('p2:', p2);
console.log('p3:', p3);
}, 100);
// 控制檯輸出
// p1: Promise {[[resolved]]: "p1"}
// p2: Promise {[[pending]]}
// p3: Promise {[[pending]]}
// p1
// p2
// error p3
// p1: Promise {[[resolved]]: "p1"}
// p2: Promise {[[resolved]]: "p2"}
// p3: Promise {[[rejected]]: "p3"}
複製代碼
p1最新建立就調用了resolve
則它的狀態馬上變爲resolved
,值爲p1,但此時p2和p3都爲pending
狀態,100毫秒後p2輸出值p2且狀態轉爲resolved
。 p3首先調用了reject
則其狀態轉爲rejected
,值爲p3,儘管下一行又調用了resolve但並無任何做用忽略成功的回調,只有error p3
。 這段實驗也顯示出Promise的一個特色promise
new Promise((resolve, reject) => {
setTimeout(() => {
resolve('a');
}, 1000);
}).then(function (value) {
console.log("第一個" + value);
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve(value + 'b');
}, 1000);
})
}).then(function (value) {
console.log("第二個" + value);
}).then(function (value) {
console.log("第三個" + value);
console.log(a);
}).then(function (value) {
console.log("第四個" + value);
}, (err) => {
console.log("第四個error", err);
})
// 第一個a
// 第二個ab
// 第三個undefined
// 第四個error ReferenceError: a is not defined
複製代碼
then
方法是Promise的實例方法,調用then
後的返回值依然是一個promise對象,注意它是全新的promise對象,通常能夠看到then
的鏈式調用,這裏須要注意區別於jQuery的鏈式調用。jQuery是返回調用對象自己。當鏈式調用時要注意不能被它繞暈了,要抓住一個重點,咱們只是在調用then
方法而已,給它傳參只是定義函數,並無執行!何時執行?是根據你的異步操做後的promise狀態如何更新以及什麼時候更新而肯定。 傳給then
的回調函數中的返回值影響着最終返回出的promise對象,參數的返回值通常有三種狀況。dom
undefined
,固然它也屬於普通同步值。則then
最終返回的是狀態是resolve
成功的Promise對象,如上段代碼的第三個輸出,它的前一個then方法內部沒有返回值則默認undefined
,接下來就直接走進第三個then
方法,且值value
就是undefined
。then
方法將根據這個Promise的狀態和值建立一個新的Promise對象返回。如第二個輸出,會等待上個then方法返回的新Promise對象狀態的更新來肯定,且會等待它的更新以及將最後的值傳過來,這種狀況也是當有多級異步操做所使用的方式。throw
一個同步異常,then
方法將返回一個rejected
狀態的Promise, 值是該異常。如第四個輸出!Promise.prototype.catch方法是then(null, rejection)的別名,用於指定發生錯誤時的回調函數。異步
let p = new Promise((resolve, reject) => {
//
});
p.then((val) => console.log('fulfilled:', val))
.catch((err) => console.log('rejected', err));
// 等同於
p.then((val) => console.log('fulfilled:', val))
.then(null, (err) => console.log("rejected:", err));
複製代碼
catch方法,它首先是捕捉處理錯誤,不管是promise調用了reject方法仍是直接拋出錯誤,都會走到catch方法內進行處理。接下來就和then方法同樣,返回的也是一個全新的Promise對象,錯誤處理的回調函數返回值一樣有三種狀況,具體看上個then方法。異步編程
let p = new Promise((resolve, reject) => {
reject('失敗')
});
p.then((val) => console.log('1then: success', val))
.then((val) => console.log('2then: success', val))
.catch((val) => console.log('3catch: error', val))
.catch((val) => console.log('4catch: error', val))
.then((val) => console.log('5then: success', val))
// 控制檯輸出
// 3catch: error 失敗
// 5then: success undefined
複製代碼
Promise 對象的錯誤具備「冒泡」性質,會一直向後傳遞,直到被捕獲爲止。也就是說,錯誤老是會被下一個catch語句捕獲。 上段代碼首先p
這個Promise對象(狀態是resolved)遇到第一個then
會忽略掉它定義的成功回調,注意此時調用完第一個then
方法後的返回值是全新的Promise對象!且狀態一樣是resolved
,爲什麼會這樣?由於它把p
的狀態進行了一層包裝也就做爲了本身的狀態,且值也和它同樣!因此說Promise
的狀態具備傳遞性。函數
由於這個錯誤目前並無被捕獲處理,因此繼續向後傳遞。一樣遇到第二個then
時咱們能夠當作跳過它,但發生的細節和第一個then
同理,直到3catch
將這個錯誤捕獲,因此輸出3catch: error 失敗
。上面也提到catch
也就是then
的一個別名而已,本質其實差很少。故此時catch
調用後的返回值再次是一個全新的promise對象,那狀態呢?由於這邊給catch
傳遞的參數並無定義返回值,因此默認就是一個同步值undefined
,則catch
返回的promise對象的狀態就是resolved
。那麼它調用最後一個then
輸出5then: success undefined
,也就不難理解了。post
let p1 = Promise.resolve('p1');
p1.then(val => console.log('success', val), val => console.log('error', val))
let p2 = Promise.reject('p2');
p2.then(val => console.log('success', val), val => console.log('error', val))
複製代碼
當傳入參數是通常同步值時則返回一個狀態爲resolve或reject的Promise對象,值也就是傳入的參數,相應的會調用成功或失敗的回調。ui
let p1 = Promise.resolve(1);
let p2 = Promise.resolve(p1);
let p3 = new Promise(function (resolve, reject) {
resolve(p1);
});
console.log(p1 === p2)
console.log(p1 === p3)
p1.then((value) => { console.log('p1=' + value)})
p2.then((value) => { console.log('p2=' + value)})
p3.then((value) => { console.log('p3=' + value)})
// 控制檯輸出:
// true
// false
// p1=1
// p2=1
// p3=1
複製代碼
當傳入一個Promise對象時,則resolve
就直接返回該Promise對象,故p1 === p2
爲true
,p3則爲全新的Promise對象,可是它狀態馬上變爲resolve
且值爲p1,它會獲取p1的狀態和值做爲本身的值。故p3=1
。
function timeout(who) {
return new Promise(function (resolve, reject) {
let wait = Math.ceil(Math.random() * 3) * 1000;
setTimeout(function () {
if (Math.random() > 0.5) {
resolve(who + ' inner success');
}
else {
reject(who + ' inner error');
}
}, wait);
console.log(who, 'wait:', wait);
});
}
let p1 = timeout('p1');
let p2 = timeout('p2');
p1.then((success) => { console.log(success) }).catch((error) => { console.log(error) })
p2.then((success) => { console.log(success) }).catch((error) => { console.log(error) })
// race只要有一個狀態改變那就當即觸發且決定總體狀態失敗仍是成功.
// all只要有一個失敗那就當即觸發總體失敗了,兩個都成功總體才成功.
Promise.all([p1, p2])
.then((...args) => {
console.log('all success', args)
})
.catch((...args) => {
console.log('someone error', args)
})
// 控制檯輸出(狀況1)
// p1 wait: 3000
// p2 wait: 1000p2 inner error
// someone error [ 'p2 inner error' ]
// p1 inner success
// 控制檯輸出(狀況2)
// p1 wait: 2000
// p2 wait: 2000
// p1 inner success
// p2 inner success
// all success [ [ 'p1 inner success', 'p2 inner success' ] ]
複製代碼
all、race方法接受數組做爲參數,且數組每一個成員都爲Promise對象。若是不是的話就調用Promise.resolve方法,將其轉爲 Promise 實例,再進一步處理。使用表示要包裝的多個promise異步操做來肯定。具體能夠看代碼理解,要多動手本身試驗!
若有錯誤或疑問歡迎指正留言: