所謂的 "promise" 代表為一個容器,保存著某個未來才會結束的事件(非同步)的結果。
Promise有兩個特點 :
1.不受外界影響。Promise有三種狀態: "pending(進行中)", "fulfilled(已成功)" , "rejected(以失敗)",只有非同步操作的結果才能改變他的狀態,其他手段無法改變。
2.狀態一旦改變就不會再變化。Promise狀態有兩種改變 : pending -> fulfilled , pending -> rejected。只要任何一種情況發生就不會再發生變化,此時稱為 "resolved(已完成)" ,就算再對她添加callback function也依然會得到這個結果。
const promise = new Promise(function(resolve,rejected){
if(/* Success */)
{
resolve(value);
}
else
{
reject(error);
}
});
Promise接收一個function作為輸入參數,該function的兩個參數分別是 "resolve" , "reject"
resolve : 當promise從未完成變成成功(pending -> resolved)時調用,將操作的結果作為參數傳遞。
rejuct : 當promise從未完成變成失敗(pending -> reject)時調用,將操作失敗的錯誤報出並傳遞。
Promise生成後可以使用.then方法分別指定resolved與reject的callback function
promise.then(function(value){
//Success
},function(error){
//false
});
then可以接受兩個callback function作為參數,當promise變為resolved時呼叫第一個function,反之當變成reject時呼叫第二個function。
Example :
timeout = (ms) => {
return new Promise((resolve,reject) =>{
//將"done"當作參數傳遞給resolve
setTimeout(resolve,ms,"done");
});
}
//傳遞給resolve的參數因為操作成功所以可以適用then讀取到參數(done)
timeout(100).then((value) => {
console.log(value); //done
});
若呼叫resolve或是reject時帶有參數,那麼他們的參數會被傳遞給callback function。reject的參數通常是error對象,表示發生的錯誤 ; resolve的參數除了正常的數值之外已可能是另一個promise
const p1 = new Promise(function (resolve, reject) {
// ...
});
const p2 = new Promise(function (resolve, reject) {
// ...
resolve(p1);
});
p1與p2都是promise,但是p2的resolve將p1做為參數,即一個非同步的操作會return另一個非同步的操作,這時p1的狀態會傳遞給p2,也就是說p1決定了p2的狀態,若p1的狀態是pending,則p2會等待p1的狀態改變;若p1已經是resolved或是rejected,那麼p2的callback function便會立刻執行。
const p1 = new Promise(function (resolve, reject) {
setTimeout(() => reject(new Error('fail')), 3000)
})
const p2 = new Promise(function (resolve, reject) {
setTimeout(() => resolve(p1), 1000)
})
p2
.then(result => console.log(result))
.catch(error => console.log(error))
// Error: fail
p1三秒後會變成reject;p2狀態在一秒之後會改變,resolve方法返回的是p1,由於p2返回的是另一個promise會導致自己的狀態無效,由p1的狀態決定了p2的狀,所以後面的.then都會變成針對 "p1" 的狀態,所以會觸發.catch console出Error:fail。
注意!! 若是調用了resolve或reject並不會結束promise的function執行。
new Promise((resolve, reject) => {
resolve(1);
console.log(2);
}).then(r => {
console.log(r);
});
若是調用了resolve(1)後,後面的console.log(2)仍然會執行並會先被console出來,因為callback function必須等到stack空了後才會執行,所以一般來說後續的操作應該放在.then裡面執行,在他們之前加上"return"以防有意外。
new Promise((resolve, reject) => {
return resolve(1);
// 后面不會執行
console.log(2);
})
為promise添加狀態改變時的callback function,第一個參數是resolved狀態的callback function,第二的參數(可選)是rejected狀態的callback function。
then會return一個新的promise(不是原來的promise),因此可以採用鏈式的寫法,then後面再調用一個then
function timeout(ms) {
return new Promise((resolve, reject) => {
resolve(1);
});
}
timeout(1000).then((value) => {
console.log(value);
return value + 10; // 將第一個callback的結果return給第二的callback當作輸入參數
}).then((value) => {
console.log(value);
});
catch用於指定發生錯誤時的callback function,若一個Promise的非同步操作發生錯誤狀態變為rejected,則會調用catch所指定的callback function處理這個錯誤,若是then方法指定的callback function執行發生錯誤的話也會被catch捕獲到。
promise發生錯誤 :
function timeout(ms) {
return new Promise((resolve, reject) => {
resolve(AAA); //產生error
});
}
timeout(1000).then((value) => {
console.log("Success");
}).catch((error) =>{
console.log(error) //ReferenceError: AAA is not defined
})
then發生錯誤 :
function timeout(ms) {
return new Promise((resolve, reject) => {
resolve(1);
});
}
timeout(1000).then((value) => {
console.log(X); //造成錯誤
}).catch((error) =>{
console.log(error) //ReferenceError: X is not defined
})
由於catch可以捕獲到promise與之後then的錯誤,所以建議對於reject來說使用catch,而不使用then的第二個參數。
finally用於不管Promise最後狀況為何都會執行的操作。(ES2018加入)
promise
.then(result => {...})
.catch(error => {...})
.finally(() => {...});
不論promise的狀態為何都會執行finally指定的callback function。
由於finally不接受任何輸入參數,所以無法知道promise的狀態為何,這鰾式finally裡面的操作式與狀態無關的,不依賴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的callback function。
2.只要p1,p2,p3其中一個變成reject那麼p就會變成reject,此時第一個變成reject的的return會傳遞給P的callback funciton。
Example :
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){
// ...
});
只有當6個promise的狀態都改變成fulfilled後,p的狀態才會改變為fulfilled,而若6個promise有其中一個變為reject那p的狀態就會變為reject。而當p的狀態改變後才會調用後面的callback function。
注意!!如果做為餐數的promise有定義自己的catch那麼一旦它變成rejected,並不會觸發到all.catch而是自己的catch
const p1 = new Promise((resolve, reject) => {
resolve('hello');
})
.then(result => result)
.catch(e => e); //定義自己的catch
const p2 = new Promise((resolve, reject) => {
throw new Error('報錯');
})
.then(result => result)
.catch(e => e); //定義自己的catch
Promise.all([p1, p2])
.then(result => console.log(result))
.catch(e => console.log(e));
上面的程式中,p1狀態會變為resolved而p2的狀態會變為reject而進入自己的catch,當p2進入自己的catch後,由於執行完catch後狀態會變為resolved,所以對all而言兩個promise都是resolved,便不會進入到all.catch指定的callback function中。
同樣是將多個promise包裝成一個新的promise但與all不同。
const p = Promise.race([p1, p2, p3]);
當p1,p2,p3任何一個發生狀態改變時,p的狀態就會跟著改變,首先改變狀態的promise會return一個參數傳入p的callback function中。
const p = Promise.race([
fetch('/resource-that-may-take-a-while'),
new Promise(function (resolve, reject) {
setTimeout(() => reject(new Error('request timeout')), 5000)
})
]);
p
.then(console.log)
.catch(console.error);
上面的程式,若秒之內fatch方法無法返回結果,p的狀態就會變成reject(當一個狀態改變時,p狀態會跟著改變),從而觸發catch中的callback function。
將現有的對象轉變為promise
Promise.resolve('foo')
// 等价于
new Promise(resolve => resolve('foo'))
resolve()的參數有四種 :
1.參數是一個promise : 若promise的參數是一個promise,則不做動作 return 一個promise
2.參數是一個thenable(具有then方法的object)
let thenable = {
then: function(resolve, reject) {
resolve(42);
}
};
pormise.resolve會將這個object轉為peomise,然後立刻執行thenable的then
let thenable = {
then: function(resolve, reject) {
resolve(42);
}
};
let p1 = Promise.resolve(thenable);
p1.then(function(value) {
console.log(value); // 42
});
上面的程式中,thanable object的then執行後,p1狀態就變為resolved,從而執行p1的then的callback function並console出42。
3.參數不具有then的object或根本不是object,則promise.resolve返回一個新的promise狀態為resolved
4.不帶任何參數,會直接返回一個resolved狀態的promise,注意!! resolve()的promise是在本輪event loop結束時才執行,而不是下一輪event loop才執行
setTimeout(function () {
console.log('three');
}, 0);
Promise.resolve().then(function () {
console.log('two');
});
console.log('one');
// one
// two
// three
由於setTimeout在下一輪event loop才執行,而promise.resolved()是在本輪的event loop結束時執行,console.log("one")則是直接放入stack中立刻執行,所以最先輸出。
Promise.reject(reason)會return一個新的promise,而該promise的狀態為rejected。
注意!!Promise.reject的參數會做完reject的理由,變成後續catch的參數。
const thenable = {
then(resolve, reject) {
reject('出错了');
}
};
Promise.reject(thenable)
.catch(e => {
console.log(e === thenable)
console.log(e);
console.log(thenable);
})
/************
true
{ then: [Function: then] }
{ then: [Function: then] }
*************/
參考資料 :
ECMAScript 6 入门