Promise學(xué)習(xí)(上):
資料: JavaScript Promise迷你書?
原著:azu / 翻譯:liubin窒所、kaku喳瓣、honnkyou?Version 1.4.1? ?/??http://liubin.org/promises-book/
概括筆記:
一衬衬、promise基本概念
1点骑、Promise是抽象異步處理對(duì)象以及對(duì)其進(jìn)行各種操作的組件刨肃。
2圃阳、Promise并不是從JavaScript中發(fā)祥的概念孽文。Promise最初被提出是在E語(yǔ)言中驻襟, 它是基于并列/并行處理設(shè)計(jì)的一種編程語(yǔ)言。
3叛溢、Promise把類似的異步處理對(duì)象和處理規(guī)則進(jìn)行規(guī)范化塑悼, 并按照采用統(tǒng)一的接口來(lái)編寫,而采取規(guī)定方法之外的寫法都會(huì)出錯(cuò)楷掉。
4厢蒜、創(chuàng)建一個(gè)promise對(duì)象
從構(gòu)造函數(shù)?Promise?來(lái)創(chuàng)建一個(gè)新建新promise對(duì)象作為接口。使用new來(lái)調(diào)用Promise的構(gòu)造器來(lái)進(jìn)行實(shí)例化promise對(duì)象烹植。
var?promise =?new?Promise(function(resolve, reject) { ?
??// 異步處理?
?// 處理結(jié)束后斑鸦、調(diào)用resolve 或 reject
});
5、promise.then()?實(shí)例回調(diào)方法
promise.then()?實(shí)例promise對(duì)象在?resolve(成功) /?reject(失敗)時(shí)調(diào)用的回調(diào)函數(shù)草雕。promise.then(onFulfilled, onRejected)巷屿。
resolve(成功)時(shí)onFulfilled?會(huì)被調(diào);reject(失敗)時(shí)onRejected?會(huì)被調(diào)用墩虹;onFulfilled嘱巾、onRejected?兩個(gè)都為可選參數(shù)。
promise.then?成功和失敗時(shí)都可以使用诫钓。 如果只想對(duì)異常進(jìn)行處理則可以采用?promise.then(undefined, onRejected)?方式菩浙,只指定reject時(shí)的回調(diào)函數(shù)勉痴。 不過promise除了then吨娜,還有catch方法巷折,捕獲失敗的處理。 promise.catch(onRejected)?惧所。promise.catch(onRejected)骤坐。
6、簡(jiǎn)單的例子
function?asyncFunction() {
? ?return?new?Promise(function?(resolve, reject) {
? ? ?setTimeout(function?() {
? ? ? ? resolve(value); ? ??
},?16); ? ?
});?
}
asyncFunction().then(function?(value) {
?console.log(value); ?
??// => value
}).catch(function?(error) {
??console.log(error);?
});
asyncFunction?函數(shù)返回promise對(duì)象下愈,?then?方法設(shè)置resolve后的回調(diào)函數(shù)纽绍,?catch方法設(shè)置發(fā)生錯(cuò)誤時(shí)的回調(diào)函數(shù)。在這種情況下 驰唬,catch?的回調(diào)函數(shù)不會(huì)被執(zhí)行(因?yàn)閜romise返回了resolve)顶岸, 如果運(yùn)行環(huán)境沒有提供?setTimeout?函數(shù)腔彰,那么上面代碼在執(zhí)行中就會(huì)產(chǎn)生異常叫编,在執(zhí)行catch?中設(shè)置的回調(diào)函數(shù)辖佣。
?* 不使用catch?方法:
我們可以使用promise.then(onFulfilled, onRejected)?方法聲明,只使用?then方法搓逾。
asyncFunction().then(function?(value) {
?? ?console.log(value);?
},function?(error) {
??console.log(error);?
});
7卷谈、實(shí)例化的promise對(duì)象的狀態(tài)。
"has-resolution" - Fulfilled :
resolve(成功)時(shí)霞篡。此時(shí)會(huì)調(diào)用?onFulfilled世蔗。
"has-rejection" - Rejected :
reject(失敗)時(shí)。此時(shí)會(huì)調(diào)用?onRejected朗兵。
"unresolved" - Pending :
既不是resolve也不是reject的狀態(tài)污淋。也就是promise對(duì)象剛被創(chuàng)建后的初始化狀態(tài)等。
其中 左側(cè)為在ES6 Promises規(guī)范中定義的術(shù)語(yǔ)余掖, 而右側(cè)則是在Promises/A+中描述狀態(tài)的術(shù)語(yǔ)寸爆。
8、promise對(duì)象的狀態(tài)盐欺,從Pending轉(zhuǎn)換為Fulfilled或Rejected之后赁豆, 這個(gè)promise對(duì)象的狀態(tài)就不會(huì)再發(fā)生任何變化。在.then?后執(zhí)行的函數(shù)只會(huì)被調(diào)用一次冗美。Fulfilled和Rejected這兩個(gè)中的任一狀態(tài)都可以表示為Settled(不變的)魔种。Settled 代表?resolve(成功) 或 reject(失敗)。
* 關(guān)鍵點(diǎn):當(dāng)promise的對(duì)象狀態(tài)發(fā)生變化時(shí)粉洼,用.then?來(lái)定義只會(huì)被調(diào)用一次的函數(shù)节预。
二、創(chuàng)建promise對(duì)象
1属韧、創(chuàng)建promise對(duì)象的流程安拟。
1.1、實(shí)例化promise對(duì)象:new Promise(fn)?返回一個(gè)promise對(duì)象
1.2挫剑、在fn?中指定異步等處理
處理結(jié)果錯(cuò)誤的話去扣,調(diào)用reject(Error對(duì)象)
處理結(jié)果正常的話,調(diào)用resolve(處理結(jié)果值)
2樊破、為promise對(duì)象添加處理方法
promise對(duì)象的處理方法有兩種:
promise對(duì)象被?resolve?時(shí)的處理(onFulfilled)
promise對(duì)象被?reject?時(shí)的處理(onRejected)
被resolve后的處理愉棱,可以在.then?方法中傳入想要調(diào)用的函數(shù)。(如下面例子)getURL函數(shù)中的?resolve(req.responseText);?會(huì)將promise對(duì)象變?yōu)閞esolve(Fulfilled)狀態(tài)哲戚, 同時(shí)使用其值調(diào)用?onFulfilled?函數(shù)奔滑。
在getURL?的處理中發(fā)生任何異常,或者被明確reject的情況下顺少, 該異常原因(Error對(duì)象)會(huì)作為.catch方法的參數(shù)被調(diào)用朋其。
例子:? 用Promise來(lái)通過異步處理方式來(lái)獲取XMLHttpRequest(XHR)的數(shù)據(jù) :
聲明一個(gè)getURL函數(shù)王浴,返回一個(gè)promise實(shí)例包裝XHR處理。
function?getURL(URL) { ?
??return?new?Promise(function?(resolve, reject) {
? ?var?req =?new?XMLHttpRequest();
? ?req.open('GET', URL,?true);
?req.onload?=?function?() {
?? ? ? ?if?(req.status ===?200) { ? ? ? ??
? ? ? ?resolve(req.responseText); ? ? ? ? ?
}?else?{
? ?reject(new?Error(req.statusText));
?? ?} ? ?
?? ?}; ? ??
? ?req.onerror?=?function?() {
?? ?reject(new?Error(req.statusText));
? ?}; ? ??
? ?req.send(); ?
??});?
}
// 運(yùn)行示例
var?URL =?"http://httpbin.org/get";
getURL(URL).then(function?onFulfilled(value){?//?被resolve后的處理梅猿,可以在.then方法中傳入想要調(diào)用的函數(shù)氓辣。
??console.log(value);
?}).catch(function?onRejected(error){?//?被reject后的處理,可以在.then 的第二個(gè)參數(shù)或者是在.catch方法中設(shè)置想要調(diào)用的函數(shù)袱蚓。
??console.error(error);
?});
getURL?只有在通過XHR取得結(jié)果狀態(tài)為200時(shí)才會(huì)調(diào)用?resolve?-其他情況(取得失敵ァ)則調(diào)用?reject?方法。
XHR發(fā)生錯(cuò)誤時(shí)?onerror?事件被觸發(fā)喇潘,調(diào)用reject体斩。發(fā)生錯(cuò)誤時(shí)需要?jiǎng)?chuàng)建一個(gè)Error對(duì)象后再將具體的值傳進(jìn)去:reject(new Error(req.statusText));?。
3颖低、?Promise.resolve
new Promise()?方法的快捷方式:靜態(tài)方法Promise.resolve(value)絮吵。
例如:
基本的用法:
new?Promise(function(resolve){ ?
??resolve(42);? //resolve(42);?會(huì)讓promise對(duì)象立即進(jìn)入確定(resolved)狀態(tài),并將 value值42?傳遞給后面then里所指定的?onFulfilled?函數(shù)
});
語(yǔ)法糖:
Promise.resolve(42);
Promise.resolve(value)?方法的返回值是一個(gè)promise對(duì)象忱屑,可以對(duì)其返回值進(jìn)行?.then?調(diào)用蹬敲。
Promise.resolve(42).then(function(value){ ??
?console.log(value);
?});
?Promise.resolve?方法的作用就是將傳遞給它的參數(shù)填充(Fulfilled)到promise對(duì)象后并返回這個(gè)promise對(duì)象。
3.1想幻、Promise.resolve?方法另一個(gè)作用就是將thenable對(duì)象轉(zhuǎn)換為promise對(duì)象
(thenable指的是一個(gè)具有?.then?方法的對(duì)象粱栖。例如jQuery.ajax())。thenable對(duì)象可以使用?Promise.resolve?來(lái)轉(zhuǎn)換為一個(gè)promise對(duì)象脏毯。就能直接使用?then?或者?catch?等在ES6 Promises里定義的方法闹究。
3.1.1、將thenable對(duì)象轉(zhuǎn)換promise對(duì)象
var?promise = Promise.resolve($.ajax('/json/comment.json'));// => promise對(duì)象
promise.then(function(value){ ?
?console.log(value);
?});
*thenable?對(duì)象我們一般用不到
4食店、 Promise.reject
Promise.reject(error)是和Promise.resolve(value)類似的靜態(tài)方法渣淤,也是?new Promise()?方法的快捷方式。
常規(guī)寫法是:
new Promise(function(resolve,reject){ ??
?reject(new Error("error"));
?});? ? ? //調(diào)用該promise對(duì)象通過then指定的?onRejected?函數(shù)吉嫩,并將錯(cuò)誤(Error)對(duì)象傳遞給 onRejected?函數(shù)价认。
語(yǔ)法糖:
Promise.reject(new?Error("error")).catch(function(error){ ?
??console.error(error);?
});
5、promise異步操作
1自娩、一般的使用情況下用踩,接收回調(diào)函數(shù)的函數(shù),根據(jù)具體的執(zhí)行情況忙迁,可以選擇是以同步還是異步的方式對(duì)回調(diào)函數(shù)進(jìn)行調(diào)用脐彩。
異步回調(diào)函數(shù)同步調(diào)用 ?
NO!!!
1、絕對(duì)不能對(duì)異步回調(diào)函數(shù)(即使在數(shù)據(jù)已經(jīng)就緒)進(jìn)行同步調(diào)用姊扔。
2惠奸、如果對(duì)異步回調(diào)函數(shù)進(jìn)行同步調(diào)用的話,處理順序可能會(huì)與預(yù)期不符恰梢,可能帶來(lái)意料之外的后果佛南。
3梗掰、對(duì)異步回調(diào)函數(shù)進(jìn)行同步調(diào)用,還可能導(dǎo)致棧溢出或異常處理錯(cuò)亂等問題嗅回。
4及穗、如果想在將來(lái)某時(shí)刻調(diào)用異步回調(diào)函數(shù)的話,可以使用?setTimeout?等異步API妈拌。
例子:
這個(gè)函數(shù)會(huì)接收一個(gè)回調(diào)函數(shù)進(jìn)行處理拥坛。
function?onReady(fn) { ??
?var?readyState = document.readyState;
??if?(readyState ===?'interactive'?|| readyState ===?'complete') { ? ?
?? ?fn(); ?
}?else?{
? ? ?window.addEventListener('DOMContentLoaded', fn);?
?? ?}
?}
?onReady(function?() {
?? ?console.log('DOM fully loaded and parsed');
?});?
console.log('==Starting==');
如果在調(diào)用onReady之前DOM已經(jīng)載入的話
如果在調(diào)用onReady之前DOM還沒有載入的話
對(duì)回調(diào)函數(shù)進(jìn)行同步調(diào)用
通過注冊(cè)?DOMContentLoaded?事件監(jiān)聽器來(lái)對(duì)回調(diào)函數(shù)進(jìn)行異步調(diào)用
因此蓬蝶,如果這段代碼在源文件中出現(xiàn)的位置不同尘分,在控制臺(tái)上打印的log消息順序也會(huì)不同。為了解決這個(gè)問題丸氛,我們可以選擇統(tǒng)一使用異步調(diào)用的方式培愁。
例子2:
setTimeout?等異步API異步調(diào)用回調(diào)函數(shù):
function?onReady(fn) { ?
??var?readyState = document.readyState;
??if?(readyState ===?'interactive'?|| readyState ===?'complete') { ? ? ?
setTimeout(fn,?0); ??
}?else?{
??window.addEventListener('DOMContentLoaded', fn); ?
??}?
}?
onReady(function?() {
?? ?console.log('DOM fully loaded and parsed');
?});
?console.log('==Starting==');
為了避免上述中同時(shí)使用同步、異步調(diào)用可能引起的混亂問題缓窜,Promise在規(guī)范上規(guī)定?Promise只能使用異步調(diào)用方式?定续。
例子三:
promise重寫上述onReady函數(shù):
function?onReadyPromise() { ?
??return?new?Promise(function?(resolve, reject) {
??var?readyState = document.readyState;
?? ?if?(readyState ===?'interactive'?|| readyState ===?'complete') { ? ? ? ? ?
??resolve(); ??
}?else?{
? ? ? ?window.addEventListener('DOMContentLoaded', resolve); ? ?
?? ?} ?
??});?
}?
onReadyPromise().then(function?() {
?console.log('DOM fully loaded and parsed');
?});?
console.log('==Starting==');
Promise能保證每次調(diào)用都是以異步方式進(jìn)行。
6禾锤、Promise 方法鏈method chain
function?taskA() { ?
??console.log("Task A");?
}
function?taskB() { ??
?console.log("Task B");?
}
function?onRejected(error) { ?
??console.log("Catch Error: A or B", error);?
}
function?finalTask() { ??
?console.log("Final Task");?
}
var?promise = Promise.resolve();
?promise
.then(taskA)
.then(taskB)
.catch(onRejected)?
.then(finalTask);
執(zhí)行流程:
6.1私股、promise chain 中如何傳遞參數(shù)
function?doubleUp(value) { ?
??return?value *?2;
?}
function?increment(value) { ?
??return?value +?1;
?}
function?output(value) { ?
??console.log(value);// => (1 + 1) * 2
}
var?promise = Promise.resolve(1);?
promise
.then(increment)
.then(doubleUp)
.then(output)
.catch(function(error){?
??// promise chain中出現(xiàn)異常的時(shí)候會(huì)被調(diào)用?
console.error(error); ??
?});
入口函數(shù)是?Promise.resolve(1);?
執(zhí)行流程:Promise.resolve(1);?傳遞 1 給?increment?函數(shù);函數(shù)?increment?對(duì)接收的參數(shù)進(jìn)行 +1 操作并返回結(jié)果恩掷,接著傳給?doubleUp?函數(shù)倡鲸;最后在函數(shù)?output?中打印結(jié)果。
* return的值會(huì)由?Promise.resolve(return的返回值);?進(jìn)行相應(yīng)的包裝處理黄娘,最終?then?的結(jié)果返回一個(gè)新創(chuàng)建的promise對(duì)象峭状。Promise#then?除了注冊(cè)一個(gè)回調(diào)函數(shù),還將回調(diào)函數(shù)的返回值進(jìn)行變換逼争,創(chuàng)建并返回一個(gè)promise對(duì)象优床。
特別注意的地方:then?返回返回新創(chuàng)建的promise對(duì)象。
例子:??then?的錯(cuò)誤使用方法
function?badAsyncCall() { ?
??var?promise = Promise.resolve();
??promise.then(function() { ? ?
?? ?// 任意處理?
?return?newVar;
?}); ??
?return?promise;
?}
正確方法:then?返回返回新創(chuàng)建的promise對(duì)象
function?anAsyncCall() { ?
??var?promise = Promise.resolve();
??return?promise.then(function() { ??
? ? ?// 任意處理?
? ?return?newVar;
?});
?}
這種函數(shù)的行為貫穿在Promise整體之中誓焦,接收一個(gè)promise對(duì)象為參數(shù)胆敞,并返回一個(gè)和接收參數(shù)不同的、新的promise對(duì)象杂伟。
7移层、 使用Promise#then同時(shí)處理多個(gè)異步請(qǐng)求
7.1、Promise.all
Promise.all?接收一個(gè) promise對(duì)象的數(shù)組作為參數(shù)稿壁,當(dāng)這個(gè)數(shù)組里的所有promise對(duì)象全部變?yōu)閞esolve或reject狀態(tài)的時(shí)候幽钢,它才會(huì)去調(diào)用?.then?方法。
例子:
function?getURL(URL) { ??
?return?new?Promise(function?(resolve, reject) {
?? ? ?var?req =?new?XMLHttpRequest();
? ?req.open('GET', URL,?true); ??
? ? ?req.onload?=?function?() {
?? ? ? ?if?(req.status ===?200) { ? ?
?? ? ? ? ? ?resolve(req.responseText); ??
}?else?{
?? ? ? ? ? ?reject(new?Error(req.statusText));
?? ? ? ? ?} ? ? ?
??}; ? ? ??
?req.onerror?=?function?() {
? ? ?reject(new?Error(req.statusText));
?? ? ?}; ?
?? ? ?req.send(); ?
??});?
}
var?request = {
? ?comment:?function?getComment() { ? ? ??
? ? ?return?getURL('http://azu.github.io/promises-book/json/comment.json').then(JSON.parse); ? ??
? ?}, ? ? ?
??people:?function?getPeople() { ? ??
? ? ? ?return?getURL('http://azu.github.io/promises-book/json/people.json').then(JSON.parse); ? ?
?? ?} ? ?
};
function?main() { ?
??return?Promise.all([request.comment(), request.people()]);
}
// 運(yùn)行示例
main().then(function?(value) {
?? ?console.log(value);
?}).catch(function(error){ ?
??console.log(error);?
});
// request.comment()?和?request.people()?會(huì)同時(shí)開始執(zhí)行傅是,而且每個(gè)promise的結(jié)果(resolve或reject時(shí)傳遞的參數(shù)值)匪燕,和傳遞給Promise.all的promise數(shù)組的順序一致蕾羊。
7.2、Promise.all?在接收到的所有的對(duì)象promise都變?yōu)?FulFilled 或者 Rejected 狀態(tài)之后才會(huì)繼續(xù)進(jìn)行后面的處理帽驯。
?與之相對(duì)的是?Promise.race?龟再。只要有一個(gè)promise對(duì)象進(jìn)入 FulFilled 或者 Rejected 狀態(tài)的話,就會(huì)繼續(xù)進(jìn)行后面的處理尼变。
暫時(shí)學(xué)習(xí)到前兩章利凑,東西太多,一時(shí)間消化不了嫌术。