本文是整理阮一峰大神ES6中 Promise 的學(xué)習(xí)筆記
目錄:
- Promise.prototype.then()
- Promise.prototype.catch()
模擬的是catch代碼塊 - Promise.prototype.finally()
- Promise.all()
全部fulfilled或第一個rejected時回調(diào) - Promise.race()
率先改變的 Promise 實例的返回值,就傳遞的回調(diào)函數(shù) - Promise.allSettled()
全部結(jié)束時回調(diào) - Promise.any()
有一個是fulfilled狀態(tài)账千,就是fulfilled狀態(tài)涌穆,所有都是rejected狀態(tài)募谎,才會是rejected狀態(tài) - Promise.resolve()
將現(xiàn)有對象轉(zhuǎn)為 Promise 對象 - Promise.reject()
- Promise.try()
模擬try代碼塊
Promise對象是一個構(gòu)造函數(shù)削祈,用來生成Promise實例
1.promise狀態(tài)狀態(tài)不受外界影響
- pending(進行中)
- fulfilled(已成功)resolved(已定型)
- rejected(已失斄撼省)
2.狀態(tài)改變详瑞,就不會再變
一旦狀態(tài)改變宪祥,就不會再變,任何時候都可以得到這個結(jié)果丑掺。Promise對象的狀態(tài)改變获印,只有兩種可能:
- 從pending變?yōu)閒ulfilled
- 從pending變?yōu)閞ejected
3.Promise缺點
- 無法取消Promise,一旦新建它就會立即執(zhí)行街州,無法中途取消兼丰。
- 不設(shè)置回調(diào)函數(shù),Promise內(nèi)部拋出的錯誤唆缴,不會反應(yīng)到外部鳍征。
- 當處于pending狀態(tài)時,無法得知目前進展到哪一個階段(剛剛開始還是即將完成)面徽。
4.代碼實例
// 創(chuàng)建promise實例
const promise = new Promise(function(resolve, reject) {
// 這里的代碼創(chuàng)建后立即執(zhí)行
// ... some code
if (/* 異步操作成功 */){
resolve(value);
} else {
reject(error);
}
});
// promise.then() 接收兩個參數(shù)回調(diào)函數(shù)艳丛,成功函數(shù),失敗函數(shù)
promise.then(function(value) {
// 全部執(zhí)行完執(zhí)行這里
// success
}, function(error) {
// failure
});
5.Promise對象實現(xiàn)的 Ajax 操作
const getJSON = function(url) {
const promise = new Promise(function(resolve, reject){
const handler = function() {
if (this.readyState !== 4) {
return;
}
if (this.status === 200) {
resolve(this.response);
} else {
reject(new Error(this.statusText));
}
};
const client = new XMLHttpRequest();
client.open("GET", url);
client.onreadystatechange = handler;
client.responseType = "json";
client.setRequestHeader("Accept", "application/json");
client.send();
});
return promise;
};
getJSON("/posts.json").then(function(json) {
console.log('Contents: ' + json);
}, function(error) {
console.error('出錯了', error);
});
6.Promise.prototype.then()
then方法是定義在原型對象Promise.prototype上的趟紊,返回的是Promise的原型對象的新實例
Promise.prototype ——> new Promise() 原實例
Promise.prototype ——> new Promise().then() 新實例
getJSON("/post/1.json")
.then(function(post) {
// 這里return的值將傳入下邊的then中
return getJSON(post.commentURL);
})
.then(function (comments) {
// comments這里的參數(shù)就是上邊return的值
console.log("resolved: ", comments);
}, function (err){
console.log("rejected: ", err);
});
7.Promise.prototype.catch()
Promise.prototype.catch與下邊等同氮双,但建議用這種方法獲取報錯,因為Promise 對象的錯誤具有“冒泡”性質(zhì)霎匈,最后的catch能獲取到之前n個then中的所有報錯信息
- .then(null, rejection)
- .then(undefined, rejection)
異步操作拋出錯誤戴差,狀態(tài)就會變?yōu)閞ejected,就會調(diào)用catch方法指定的回調(diào)函數(shù),可以寫多個catch和多個then,順序隨意
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));
Promise 會吃掉錯誤
Promise 內(nèi)部的錯誤不會影響到 Promise 外部的代碼
const someAsyncThing = function() {
return new Promise(function(resolve, reject) {
// 下面一行會報錯铛嘱,因為x沒有聲明
resolve(x + 2);
});
};
someAsyncThing().then(function() {
console.log('everything is great');
});
setTimeout(() => { console.log(123) }, 2000);
// Uncaught (in promise) ReferenceError: x is not defined
// 123
8.Promise.prototype.finally()
- finally方法用于指定不管 Promise 對象最后狀態(tài)如何暖释,都會執(zhí)行的操作袭厂。
- finally方法的回調(diào)函數(shù)不接受任何參數(shù)
- 沒有辦法知道,前面的 Promise 狀態(tài)到底是fulfilled還是rejected球匕。
- finally方法里面的操作纹磺,與狀態(tài)無關(guān)的,不依賴于 Promise 的執(zhí)行結(jié)果
promise
.finally(() => {
// 語句
});
// 等同于
promise
.then(
result => {
// 語句
return result;
},
error => {
// 語句
throw error;
}
);
9.Promise.all()
Promise.all()方法用于將多個 Promise 實例亮曹,包裝成一個新的 Promise 實例
- Promise.all()方法接受一個數(shù)組作為參數(shù)橄杨,p1、p2乾忱、p3都是 Promise 實例
const p = Promise.all([p1, p2, p3]);
p的狀態(tài)由p1讥珍、p2、p3決定窄瘟,分成兩種情況衷佃。
(1)只有p1、p2蹄葱、p3的狀態(tài)都變成fulfilled氏义,p的狀態(tài)才會變成fulfilled,此時p1图云、p2惯悠、p3的返回值組成一個數(shù)組,傳遞給p的回調(diào)函數(shù)竣况。
(2)只要p1克婶、p2、p3之中有一個被rejected丹泉,p的狀態(tài)就變成rejected情萤,此時第一個被reject的實例的返回值,會傳遞給p的回調(diào)函數(shù)摹恨。
// 生成一個Promise對象的數(shù)組
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 實例的數(shù)組筋岛,只有這 6 個實例的狀態(tài)都變成fulfilled,或者其中有一個變?yōu)閞ejected晒哄,才會調(diào)用Promise.all方法后面的回調(diào)函數(shù)睁宰。
const databasePromise = connectDatabase();
const booksPromise = databasePromise
.then(findAllBooks);
const userPromise = databasePromise
.then(getCurrentUser);
Promise.all([
booksPromise,
userPromise
])
.then(([books, user]) => pickTopRecommendations(books, user));
上面代碼中,booksPromise和userPromise是兩個異步操作寝凌,只有等到它們的結(jié)果都返回了柒傻,才會觸發(fā)pickTopRecommendations這個回調(diào)函數(shù)。
注意:如果作為參數(shù)的 Promise 實例较木,自己定義了catch方法诅愚,那么它一旦被rejected,并不會觸發(fā)Promise.all()的catch方法。
const p1 = new Promise((resolve, reject) => {
resolve('hello');
})
.then(result => result)
.catch(e => e);
const p2 = new Promise((resolve, reject) => {
throw new Error('報錯了');
})
.then(result => result)
.catch(e => e);
Promise.all([p1, p2])
.then(result => console.log(result))
.catch(e => console.log(e));
// ["hello", Error: 報錯了]
上面代碼中:
p1會resolved违孝,p2首先會rejected,但是p2有自己的catch方法泳赋,該方法返回的是一個新的 Promise 實例雌桑,p2指向的實際上是這個實例。該實例執(zhí)行完catch方法后祖今,也會變成resolved校坑,導(dǎo)致Promise.all()方法參數(shù)里面的兩個實例都會resolved,因此會調(diào)用then方法指定的回調(diào)函數(shù)千诬,而不會調(diào)用catch方法指定的回調(diào)函數(shù)耍目。
10.Promise.race()
Promise.race()方法同樣是將多個 Promise 實例,包裝成一個新的 Promise 實例
const p = Promise.race([p1, p2, p3]);
上面代碼中徐绑,只要p1邪驮、p2、p3之中有一個實例率先改變狀態(tài)傲茄,p的狀態(tài)就跟著改變毅访。那個率先改變的 Promise 實例的返回值,就傳遞給p的回調(diào)函數(shù)盘榨。
11.Promise.allSettled()
確保所有操作都結(jié)束
Promise.allSettled()方法接受一組 Promise 實例作為參數(shù)喻粹,包裝成一個新的 Promise 實例。只有等到所有這些參數(shù)實例都返回結(jié)果草巡,不管是fulfilled還是rejected守呜,包裝實例才會結(jié)束
const promises = [
fetch('/api-1'),
fetch('/api-2'),
fetch('/api-3'),
];
await Promise.allSettled(promises);
removeLoadingIndicator();
上面代碼對服務(wù)器發(fā)出三個請求,等到三個請求都結(jié)束山憨,不管請求成功還是失敗查乒,加載的滾動圖標就會消失。
12.Promise.any()
只要參數(shù)實例有一個變成fulfilled狀態(tài)萍歉,包裝實例就會變成fulfilled狀態(tài)侣颂;如果所有參數(shù)實例都變成rejected狀態(tài),包裝實例就會變成rejected狀態(tài)枪孩。
const promises = [
fetch('/endpoint-a').then(() => 'a'),
fetch('/endpoint-b').then(() => 'b'),
fetch('/endpoint-c').then(() => 'c'),
];
try {
const first = await Promise.any(promises);
console.log(first);
} catch (error) {
console.log(error);
}
上面代碼中憔晒,Promise.any()方法的參數(shù)數(shù)組包含三個 Promise 操作。其中只要有一個變成fulfilled蔑舞,Promise.any()返回的 Promise 對象就變成fulfilled拒担。如果所有三個操作都變成rejected,那么就會await命令就會拋出錯誤
13.Promise.resolve()
將現(xiàn)有對象轉(zhuǎn)為 Promise 對象
Promise.resolve('foo')
// 等價于
new Promise(resolve => resolve('foo'))
參數(shù):
- 參數(shù)是一個 Promise 實例
那么Promise.resolve將不做任何修改攻询、原封不動地返回這個實例从撼。 - 參數(shù)是一個thenable對象
thenable對象指的是具有then方法的對象,比如下面這個對象钧栖。 - 參數(shù)不是具有then方法的對象低零,或根本就不是對象
- 不帶有任何參數(shù)
let thenable = {
then: function(resolve, reject) {
resolve(42);
}
};
let p1 = Promise.resolve(thenable);
p1.then(function(value) {
console.log(value); // 42
});
注意: 立即resolve()的 Promise 對象婆翔,是在本輪“事件循環(huán)”(event loop)的結(jié)束時執(zhí)行,而不是在下一輪“事件循環(huán)”的開始時掏婶。
setTimeout(function () {
console.log('three');
}, 0);
Promise.resolve().then(function () {
console.log('two');
});
console.log('one');
// one
// two
// three
setTimeout(fn, 0)在下一輪“事件循環(huán)”開始時執(zhí)行
Promise.resolve()在本輪“事件循環(huán)”結(jié)束時執(zhí)行
console.log('one')則是立即執(zhí)行啃奴,因此最先輸出
14.Promise.reject()
const p = Promise.reject('出錯了');
// 等同于
const p = new Promise((resolve, reject) => reject('出錯了'))
注意,Promise.reject()方法的參數(shù)雄妥,會原封不動地作為reject的理由最蕾,變成后續(xù)方法的參數(shù)。這一點與Promise.resolve方法不一致老厌。
const thenable = {
then(resolve, reject) {
reject('出錯了');
}
};
Promise.reject(thenable)
.catch(e => {
console.log(e === thenable)
})
// true
15.Promise.try()
事實上瘟则,Promise.try就是模擬try代碼塊,就像promise.catch模擬的是catch代碼塊枝秤。
const f = () => console.log('now');
Promise.try(f);
console.log('next');
// now
// next