pomise整理,boom!boom!boom!
promise:設計目的,為了解決非同步操作。
promise兩個特點
一,只有非同步操作的結果,可以決定當前是哪一種狀態,任何其他操作都無法改變這個狀態
( promise三種狀態:1.pending
(進行中)、fulfilled
(已成功)和rejected
(已失敗))
二,一旦狀態改變,就不會再變,任何時候都可以得到這個結果。
只有兩種可能:從pending
變為fulfilled
和從pending
變為rejected
。只要這兩種情況發生,狀態就凝固了,不會 再變 了,會一直保持這個結果,這時就稱為 resolved(已定型)
簡單的promise:(這裡推薦使用catch所以就不寫then第二個引數的形式了)
const promise = new Promise(function(resolve, reject) { // ... some code if (/* 非同步操作成功 */){ resolve(value); } else { reject(error); } }); promise.then(function(value) { // success }).catch(function(error) { // failure });
resolve
函式的作用是,將Promise
物件的狀態從“未完成”變為“成功”,在非同步操作成功時呼叫,並將非同步操作的結果,作為引數傳遞出去;由promise.then接收resolve傳遞過來的引數
reject
函式的作用是,將Promise
物件的狀態從“未完成”變為“失敗”,在非同步操作失敗時呼叫,並將非同步操作報出的錯誤,作為引數傳遞出去。由promise.catch接收reject傳遞過來的引數
注意:
呼叫resolve
或reject
並不會終結 Promise 的引數函式的執行。
new Promise((resolve, reject) => { resolve(1); console.log(2); }).then(r => { console.log(r); }); // 2 // 1
上面程式碼中,呼叫resolve(1)
以後,後面的console.log(2)
還是會執行,並且會首先打印出來。這是因為立即 resolved 的 Promise 是在本輪事件迴圈的末尾執行,總是晚於本輪迴圈的同步任務。
一般來說,呼叫resolve
或reject
以後,Promise 的使命就完成了,後繼操作應該放到then
方法裡面,而不應該直接寫在resolve
或reject
的後面。所以,最好在它們前面加上return
語句,這樣就不會有意外。
new Promise((resolve, reject) => {
return resolve(1);
// 後面的語句不會執行
console.log(2);
})
Promise.prototype.then() === Promise.then()
then
方法返回的是一個新的Promise
例項(注意,不是原來那個Promise
例項)。因此可以採用鏈式寫法,即then
方法後面再呼叫另一個then
方法。第一個回撥函式完成以後,會將返回結果作為引數,傳入第二個回撥函式。
promise(function(resolve, reject) {resolve('ok')})
.then(function(json) {
return json.post;
}).then(function(post) {
// ...
});
Promise.prototype.catch() === Promise.catch()
如果非同步操作丟擲錯誤,狀態就會變為rejected
,就會呼叫catch
方法指定的回撥函式,處理這個錯誤。另外,then
方法指定的回撥函式,如果執行中丟擲錯誤,也會被catch
方法捕獲。
例子:
const promise = new Promise(function(resolve, reject) {
throw new Error('test');
});
promise.then(function(value){
console.log(value) //這個log不會被呼叫
}).catch(function(error) {
console.log(error);
});
// Error: test
如果 Promise 狀態已經變成resolved
,再丟擲錯誤是無效的。
const promise = new Promise(function(resolve, reject) {
resolve('ok');
throw new Error('test');
});
promise
.then(function(value) { console.log(value) })
.catch(function(error) { console.log(error) });
// ok
Promise 物件的錯誤具有“冒泡”性質,會一直向後傳遞,直到被捕獲為止。也就是說,錯誤總是會被下一個catch
語句捕獲。(getJSON()是一個封裝好的promise的ajax呼叫)
getJSON('/post/1.json').then(function(post) {
return getJSON(post.commentURL);
}).then(function(comments) {
// some code
}).catch(function(error) {
// 處理前面三個Promise產生的錯誤
});
上面程式碼中,一共有三個 Promise 物件:一個由getJSON
產生,兩個由then
產生。它們之中任何一個丟擲的錯誤,都會被最後一個catch
捕獲。
catch
方法返回的還是一個 Promise 物件,因此後面還可以接著呼叫then
方法。
例:
const someAsyncThing = function() {
return new Promise(function(resolve, reject) {
// 下面一行會報錯,因為x沒有宣告
resolve(x + 2);
});
};
someAsyncThing()
.catch(function(error) {
console.log('oh no', error);
})
.then(function() {
console.log('carry on');
});
// oh no [ReferenceError: x is not defined]
// carry on
Promise.prototype.finally()
finally
方法用於指定不管 Promise 物件最後狀態如何,都會執行的操作。
例:
promise
.then(result => {···})
.catch(error => {···})
.finally(() => {···});
上面程式碼中,不管promise
最後的狀態,在執行完then
或catch
指定的回撥函式以後,都會執行finally
方法指定的回撥函式。finally
方法的回撥函式不接受任何引數,這意味著沒有辦法知道,前面的 Promise 狀態到底是fulfilled
還是rejected
。這表明,finally
方法裡面的操作,應該是與狀態無關的,不依賴於 Promise 的執行結果。
Promise.all()
Promise.all
方法用於將多個 Promise 例項,包裝成一個新的 Promise 例項。
例:
const p = Promise.all([p1, p2, p3]);
p
的狀態由p1
、p2
、p3
決定,分成兩種情況。
(1)只有p1
、p2
、p3
的狀態都變成fulfilled(成功狀態)
,p
的狀態才會變成fulfilled(成功狀態)
,此時p1
、p2
、p3
的返回值組成一個數組,傳遞給p
的回撥函式。
(2)只要p1
、p2
、p3
之中有一個被rejected(失敗)
,p
的狀態就變成rejected(失敗)
,此時第一個被reject
的例項的返回值,會傳遞給p
的回撥函式。
例:
// 生成一個Promise物件的陣列
const promises = [2, 3, 5, 7, 11, 13].map(function (id) {
return getJSON('/post/' + id + ".json");
});
Promise.all(promises).then(function (posts) {
// ...
}).catch(function(reason){
// ...
});
上面程式碼中,promises
是包含 6 個 Promise 例項的陣列,只有這 6 個例項的狀態都變成fulfilled
,或者其中有一個變為rejected
,才會呼叫Promise.all
方法後面的回撥函式(then和catch)。
注意:
如果作為引數的 Promise 例項,自己定義了catch
方法,那麼它一旦被rejected
,並不會觸發Promise.all()
的catch
方法。
Promise.resolve()
有時需要將現有物件轉為 Promise 物件,Promise.resolve
方法就起到這個作用。
Promise.resolve
方法的引數分成四種情況。
(1)引數是一個 Promise 例項
如果引數是 Promise 例項,那麼Promise.resolve
將不做任何修改、原封不動地返回這個例項。
(2)引數是一個thenable
物件
thenable
物件指的是具有then
方法的物件,比如下面這個物件。
let thenable = {
then: function(resolve, reject) {
resolve(42);
}
};
(3)引數不是具有then
方法的物件,或根本就不是物件
如果引數是一個原始值,或者是一個不具有then
方法的物件,則Promise.resolve
方法返回一個新的 Promise 物件,狀態為resolved
。
(4)不帶有任何引數
Promise.resolve
方法允許呼叫時不帶引數,直接返回一個resolved
狀態的 Promise 物件。
所以,如果希望得到一個 Promise 物件,比較方便的方法就是直接呼叫Promise.resolve
方法。
本來想著整理到這裡就結束了,感覺基本常用的方法就這些了,但是去了某大公司面試的時候,人家還是問了全部的方法,本著程式設計師縝密的思想還是多整理整理吧,(手動滑稽~)
Promise.race()
Promise.race
方法同Promise.all一樣,但狀態改變有些差距
const p = Promise.race([p1, p2, p3]);
上面程式碼中,只要p1
、p2
、p3
之中有一個例項率先改變狀態,p
的狀態就跟著改變。那個率先改變的 Promise 例項的返回值,就傳遞給p
的回撥函式。
Promise.reject()
Promise.reject(reason)
方法也會返回一個新的 Promise 例項,該例項的狀態為rejected(執行失敗)
。
const p = Promise.reject('出錯了');
最後帶來應用:
載入圖片
我們可以將圖片的載入寫成一個Promise
,一旦載入完成,Promise
的狀態就發生變化。
const preloadImage = function (path) {
return new Promise(function (resolve, reject) {
const image = new Image();
image.onload = resolve;
image.onerror = reject;
image.src = path;
});
};