什么是Promise
Promise是異步編程的一種解決方案馁菜,比傳統(tǒng)的異步解決方案【回調(diào)函數(shù)】和【事件】更合理、更強(qiáng)大峭火。
- 從語(yǔ)法上講智嚷,Promise是一個(gè)對(duì)象盏道,從它可以獲取異步操作的信息。
代碼書(shū)寫(xiě)比較
首先封裝一個(gè)支持Promise的ajax方法:
function request(url,data = {}){
return new Promise((resolve,reject)=>{
$.ajax({
url,
data,
success:function (data) {
resolve(data);
},
error:function (error) {
reject(error);
}
})
});
}衅枫。
用 request 方法實(shí)現(xiàn)多個(gè)互相依賴的網(wǎng)絡(luò)請(qǐng)求
let url1 = 'http://xxx.xxx.1';
let url2 = 'http://xxx.xxx.2';
let url3 = 'http://xxx.xxx.3';
request(url1)
.then((data)=>{
console.log(data);
return request(url2,data)
})
.then((data)=>{
console.log(data);
return request(url3,data)
})
.then((data)=>{
console.log(data)
})
.catch((error)=>{
console.log(error);
});
Promise 的特性
Promise 的狀態(tài)弦撩、
- pending (等待態(tài))
- fulfilled (完成態(tài))
- rejected (拒絕態(tài))
終值與拒因
- 終值: 指的是promise被解決時(shí)傳遞給解決回掉的值
- 拒因:拒絕原因腕让,指在 promise 被拒絕時(shí)傳遞給異炒客瑁回調(diào)的拒絕原因
狀態(tài)與狀態(tài)關(guān)系静袖,狀態(tài)與終值和拒因的關(guān)系
- pending可以遷移至fulfilled或rejected
- fulfilled 不能遷移至其他狀態(tài)队橙,必須擁有一個(gè)不可變的終值
- rejected 不能遷移至其他狀態(tài),必須擁有一個(gè)不可變的據(jù)因
Promise 的使用
構(gòu)造函數(shù)
Promise 是一個(gè)構(gòu)造函數(shù)仇矾,使用new操作符返回一個(gè)promise對(duì)象
構(gòu)造函數(shù)接收一個(gè) excutor函數(shù)作為參數(shù)
excutor函數(shù)有兩個(gè)函數(shù)類型的參數(shù)resolve和reject
let p = new Promise((resolve,reject)=>{
// 在 excutor 函數(shù)中執(zhí)行異步操作
// 當(dāng)異步操作完成后贮匕,調(diào)用 resolve 函數(shù)或 reject 函數(shù)
});
- 構(gòu)造函數(shù)在調(diào)用時(shí)花枫,excutor函數(shù)會(huì)作為同步代碼立即執(zhí)行
- 我們通常在excutor函數(shù)中執(zhí)行我們的異步操作
- 未調(diào)用resolve、reject函數(shù)時(shí)敦锌,promise對(duì)象的狀態(tài)為pending
let p1 = new Promise((resolve,reject)=>{
setTimeout(()=>{
console.log('p1');
},1000);
});
// p1 的狀態(tài)一直為 pending
- 當(dāng)調(diào)用resolve函數(shù)乙墙,resolve的參數(shù)為非promise對(duì)象伶丐,非thenable對(duì)象
- resolve 函數(shù)的參數(shù),作為 promise 對(duì)象的終值
- promise 對(duì)象的狀態(tài)變?yōu)?fulfilled
let p2 = new Promise((resolve,reject)=>{
setTimeout(()=>{
console.log('p2');
resolve('我是p2的終值')
},1000);
});
// 代碼執(zhí)行肛走,1000ms內(nèi)录别,p2 的狀態(tài)為 pending
// 代碼執(zhí)行组题,1000ms后,p2 的狀態(tài)為 fulfilled
// 代碼執(zhí)行梢褐,1000ms后赵讯,p2 的終值為 '我是p2的終值'
- 當(dāng)調(diào)用 resolve 函數(shù), resolve 的參數(shù)為 promise 對(duì)象
- promise 對(duì)象的狀態(tài)边翼、終值、拒因與傳入的 promise 對(duì)象同步
let p = new Promise((resolve,reject)=>{
reject('error')
})
let p1 = new Promise((resolve,reject)=>{
resolve(p)
})
// p1 的狀態(tài)為 rejected 丈积,拒因?yàn)?error
- 當(dāng)調(diào)用 resolve 函數(shù), resolve 的參數(shù)為 thenable 對(duì)象
- 會(huì)對(duì) thenable 對(duì)象進(jìn)行展開(kāi)操作江滨,promise 對(duì)象的狀態(tài)厌均、終值、拒因取決于 thenable 對(duì)象的 then 方法調(diào)用結(jié)果
let thenable1 = {
then:function(resolve,reject){
resolve(1)
}
}
let thenable2 = {
then:function(resolve,reject){
reject(2)
}
}
let thenable3 = {
then:function(resolve,reject){
throw new Error(3)
}
}
let thenable4 = {
then:function(fn1,fn2){
//不調(diào)用 fn1 fn2
}
}
let p1 = new Promise((resolve,reject)=>{
resolve(thenable1);
})
let p2 = new Promise((resolve,reject)=>{
resolve(thenable2);
})
let p3 = new Promise((resolve,reject)=>{
resolve(thenable3);
})
let p4 = new Promise((resolve,reject)=>{
resolve(thenable4);
})
// p1 的狀態(tài)為 fulfilled 終值為 1
// p2 的狀態(tài)為 rejected 終值為 2
// p3 的狀態(tài)為 rejected 拒因?yàn)?Error:3
// p4 的狀態(tài)為 pending
- 當(dāng)調(diào)用reject函數(shù)悔详,reject函數(shù)的參數(shù)惹挟,作為promise對(duì)象的拒因
- promise對(duì)象的狀態(tài)變?yōu)閞ejected
let p3 = new Promise((resolve,reject)=>{
setTimeout(()=>{
console.log('p3');
reject('我是p3的拒因')
},1000);
});
// 代碼執(zhí)行,1000ms內(nèi)归苍,p3 的狀態(tài)為 pending
// 代碼執(zhí)行运怖,1000ms后摇展,p3 的狀態(tài)為 rejected
// 代碼執(zhí)行咏连,1000ms后祟滴,p3 的拒因?yàn)?'我是p3的拒因'
Promise對(duì)象上的方法
then方法
promise提供一個(gè)then方法,用于訪問(wèn)其終值和拒因骑晶。
promise的then 方法接受兩個(gè)參數(shù):
promise.then(onFulfilled, onRejected)
- onFulfilled函數(shù)用于當(dāng)promise狀態(tài)變?yōu)閒ulfilled時(shí)草慧,接收終值冠蒋。
- onRejected函數(shù)用于當(dāng)promise狀態(tài)變?yōu)閞ejected時(shí)乾胶,接收拒因
new Promise((resolve,reject)=>{
setTimeout(()=>{
resolve('異步任務(wù)獲取的數(shù)據(jù)')
},50)
}).then((data)=>{
console.log(data)
})
// 異步任務(wù)獲取的數(shù)據(jù)
new Promise((resolve,reject)=>{
setTimeout(()=>{
reject(new Error('異步任務(wù)異常'))
},50)
}).then(null,(error)=>{
console.log(error)
})
// Error: 異步任務(wù)異常
new Promise((resolve,reject)=>{
throw new Error('拋出一個(gè)異常');
}).then(null,(error)=>{
console.log(error)
})
// Error: 拋出一個(gè)異常
onFulfilled 和 onRejected 參數(shù)可選
- 如果 onFulfilled 不是函數(shù)识窿,其必須被忽略
- 如果 onRejected 不是函數(shù)喻频,其必須被忽略
onFulfilled 的特性
如果onFulfilled是函數(shù):
- 當(dāng)promise執(zhí)行結(jié)束后其必須被調(diào)用,其第一個(gè)參數(shù)為promise的終值
- 當(dāng)promise執(zhí)行結(jié)束前其不可被調(diào)用
- 其調(diào)用次數(shù)不可超過(guò)1次
onRejected 的特性
如果onRejected是函數(shù):
- 當(dāng)promise執(zhí)行結(jié)束后其必須被調(diào)用锻煌,其第一個(gè)參數(shù)為promise的拒因
- 當(dāng)promise執(zhí)行結(jié)束前其不可被調(diào)用
- 其調(diào)用次數(shù)不可超過(guò)1次
onFulfilled 和 onRejected 的調(diào)用時(shí)機(jī)
- 當(dāng) promise 對(duì)象的狀態(tài)變?yōu)?fulfilled 或 rejected 時(shí)調(diào)用
- onFulfilled宋梧、onRejected 永遠(yuǎn)都是異步調(diào)用
- onFulfilled、onRejected 在事件隊(duì)列中作為微任務(wù)來(lái)處理
console.log(1);
setTimeout(function(){
console.log(2)
},0)
new Promise((resolve,reject)=>{
resolve(3);
}).then((data)=>{
console.log(data);
})
console.log(4)
// print: 1 4 3 2
onFulfilled 和 onRejected 的調(diào)用要求
- onFulfilled 和 onRejected 必須被作為函數(shù)調(diào)用
- 非嚴(yán)格模式下释涛,this 為全局對(duì)象
- 嚴(yán)格模式下唇撬,this 為 undefined
function fn1(){
new Promise((resolve)=>{
resolve();
}).then(function(){
console.log(this)
})
}
function fn2(){
"use strict";
new Promise((resolve)=>{
resolve();
}).then(function(){
console.log(this)
})
}
fn1(); // print: window
fn2(); // print: undefined
then方法的多次調(diào)用
- then方法可以被同一個(gè)promise對(duì)象多次調(diào)用
- then方法會(huì)返回一個(gè)新的promise對(duì)象
- 當(dāng)promise成功執(zhí)行時(shí)窖认,所有onFulfilled需按照其注冊(cè)順序依次回調(diào)
- 當(dāng) promise 被拒絕執(zhí)行時(shí)告希,所有的 onRejected 需按照其注冊(cè)順序依次回調(diào)
let p = new Promise((resolve)=>{
resolve()
});
let p1 = p.then(()=>{
console.log('異步執(zhí)行暂雹,第一個(gè)onFulfilled');
});
let p2 = p.then(()=>{
console.log('異步執(zhí)行,第二個(gè)onFulfilled');
});
console.log(p1.constructor === Promise);
console.log(p === p1);
console.log(p === p2);
console.log(p1 === p2);
// print: true
// print: false
// print: false
// print: false
// print: 異步執(zhí)行仙逻,第一個(gè)onFulfilled
// print: 異步執(zhí)行系奉,第二個(gè)onFulfilled
then方法的返回值
then方法返回一個(gè)promise對(duì)象
promise2 = promise1.then(onFulfilled, onRejected);
- 若onFulfilled 姑廉、onRejected 返回一個(gè)非promise
對(duì)象桥言、非thenable對(duì)象的值x,則promise2的狀態(tài)變?yōu)閒ulfilled,終值為x
let p = new Promise((resolve,reject)=>{
throw new Error();
});
let p1 = p.then(null,(data)=>{
return '我是p2的終值'
});
p1.then((data)=>{
console.log(data)
});
// print: 我是p2的終值
- 若 onFulfilled 并鸵、onRejected 返回一個(gè) promise 對(duì)象的值 x 园担,promise2 的狀態(tài)、終值艰山、拒因與 x 同步
let p1 = new Promise((resolve,reject)=>{
resolve(1)
})
let p2 = new Promise((resolve,reject)=>{
reject(2)
})
let p3 = new Promise((resolve)=>{
resolve()
})
let p4 = p3.then(()=>{
return p1;
})
let p5 = p3.then(()=>{
return p2;
})
// p4 的狀態(tài)為 fulfilled 終值為 1
// p5 的狀態(tài)為 rejected 拒因?yàn)?2
- 若 onFulfilled 咏闪、onRejected 返回一個(gè) thenable 對(duì)象 汤踏,會(huì)對(duì) thenable 對(duì)象進(jìn)行展開(kāi)操作,promise2 的狀態(tài)搂擦、終值瀑踢、拒因取決于 thenable 對(duì)象的 then 方法調(diào)用結(jié)果
let thenable1 = {
then:function(resolve,reject){
resolve(1)
}
}
let thenable2 = {
then:function(resolve,reject){
reject(2)
}
}
let p1 = new Promise((resolve,reject)=>{
resolve()
})
let p2 = p1.then(()=>{
return thenable1;
})
let p3 = p1.then(()=>{
return thenable2;
})
// p2 的狀態(tài)為 fulfilled 終值為 1
// p3 的狀態(tài)為 rejected 拒因?yàn)?2
- 若 onFulfilled 或者 onRejected 拋出一個(gè)異常 e 橱夭,則 promise2 的狀態(tài)為 rejected桑逝,拒因?yàn)?e
let p = new Promise((resolve,reject)=>{
resolve();
});
let p1 = p.then((data)=>{
throw new Error('error')
});
p1.then(null,(err)=>{
console.log(err);
});
// print: Error: error
- 若 onFulfilled 不是函數(shù)且 promise1 成功執(zhí)行楞遏, promise2 的狀態(tài)為 fulfilled 終值為 promise1 的終值
let p = new Promise((resolve,reject)=>{
resolve('我是p1的終值');
});
let p1 = p.then(null,null);
p1.then((data)=>{
console.log(data);
});
// print: 我是p1的終值
- 若 onRejected 不是函數(shù)且 promise1 拒絕執(zhí)行, promise2 的狀態(tài)為 rejected 拒因?yàn)?promise1 的拒因
let p = new Promise((resolve,reject)=>{
reject('我是p1的拒因');
});
let p1 = p.then(null,null);
p1.then(null,(err)=>{
console.log(err);
});
// print:我是p1的拒因
- 若 onFulfilled糙俗、onRejected 執(zhí)行過(guò)程中拋出異常巧骚,則 promise2 的狀態(tài)為 rejected 拒因?yàn)閽伋龅漠惓?/li>
let p = new Promise((resolve,reject)=>{
resolve('我是p的終值');
});
let p1 = p.then((data)=>{
throw new Error('異常')
});
p1.then(null,(err)=>{
console.log(err);
});
// print:Error: 異常
終值和拒因的穿透特性
- 如果 promise 的狀態(tài)變?yōu)?fulfilled格二,then 方法沒(méi)有注冊(cè) onFulfilled
- then 方法返回的 promise 對(duì)象的狀態(tài)變?yōu)?fulfilled
- then 方法返回的 promise 對(duì)象的終值與原 promise 對(duì)象的終值相同
- 如果 promise 的狀態(tài)變?yōu)?rejected蟋定,then 方法沒(méi)有注冊(cè) onRejected
- then 方法返回的 promise 對(duì)象的狀態(tài)變?yōu)?rejected
- then 方法返回的 promise 對(duì)象的拒因與原 promise 對(duì)象的拒因相同
let p1 = new Promise((resolve,reject)=>{
resolve(1)
})
let p2 = new Promise((resolve,reject)=>{
reject(2)
})
let p3 = p1.then(null,null);
let p4 = p2.then(null,null);
// p3 的狀態(tài)是 fulfilled 終值 1
// p4 的狀態(tài)是 rejected 拒因 2
p5 = p3.then(null,null);
p6 = p4.then(null,null);
// p3 的狀態(tài)是 fulfilled 終值 1
// p4 的狀態(tài)是 rejected 拒因 2
- 穿透特性主要用于異常處理
let fn1 = function(){}
let fn2 = function(){}
let fn3 = function(){}
let fn4 = function(){}
let fn5 = function(){}
let onError = function(){};
new Promise((resolve,reject)=>{
setTimeout(function(){
reject()
})
})
.then(fn1)
.then(fn2)
.then(fn3)
.then(fn4)
.then(fn5)
.then(null,onError)
fn1驶兜、fn2抄淑、fn3、fn4矗愧、fn5 都可能發(fā)生錯(cuò)誤郑原,通過(guò)在最后的then函數(shù)注冊(cè)的 onRejected 函數(shù)接收可能發(fā)生異常錯(cuò)誤
catch方法:
catch(fn) 方法實(shí)際上是 then(null,fn) 方法的別名犯犁,catch 方法的返回值以及 catch 方法中出現(xiàn)異常的情況與調(diào)用 then 方法相同
new Promise((resolve,reject)=>{
reject()
}).then(null,function(error){
})
// 等同于
new Promise((resolve,reject)=>{
reject()
}).catch(function(error){
})
Promise 的靜態(tài)方法
Promise.resolve
Promise.resolve 方法用于將現(xiàn)有數(shù)據(jù)轉(zhuǎn)換為 promise 對(duì)象
- 若入?yún)?promise 對(duì)象
返回的 promise 對(duì)象的狀態(tài)酸役、終值、拒因與 Promise.resolve 方法的入?yún)⑼?/p>
- 若入?yún)?thenable 對(duì)象
會(huì)對(duì) thenable 對(duì)象進(jìn)行展開(kāi)操作贱呐,返回的 promise 對(duì)象的狀態(tài)入桂、終值抗愁、拒因取決于 thenable 對(duì)象的 then 方法調(diào)用結(jié)果
- 若入?yún)榉?promise 非 thenable 對(duì)象
1.返回的 promise 對(duì)象的狀態(tài)為 fulfilled
2.返回的 promise 對(duì)象的終值為 Promise.resolve 方法的入?yún)?/p>
let p = Promise.resolve(x)
// 等價(jià)于
let p = new Promise((resolve)=>{
resolve(x)
})
Promise.reject
- Promise.reject 方法用于返回一個(gè)狀態(tài)為 rejected ,拒因?yàn)榉椒ㄈ雲(yún)⒌?promise 對(duì)象
let p = Promise.reject(x)
// 等價(jià)于
let p = new Promise((resolve,reject)=>{
reject(x)
})
Promise.all
- Promise.all 方法用于將多個(gè) promise 對(duì)象包裝成一個(gè)新的 promise 對(duì)象
const p = Promise.all([p1, p2, p3]);
p1驹愚、p2、p3 都是 promise 對(duì)象谁鳍,如果不是劫瞳,調(diào)用 Promise.resolve 方法轉(zhuǎn)換為 promise 對(duì)象
p 的狀態(tài)由 p1志于、p2、p3 決定當(dāng) p1养泡、p2、p3 的狀態(tài)都變成 fulfilled
p 的狀態(tài)為 fulfilled
此時(shí) p1购披、p2刚陡、p3 的終值組成一個(gè)數(shù)組株汉,這個(gè)數(shù)組作為 p 的終值
- 當(dāng) p1乔妈、p2、p3 的狀態(tài)有一個(gè)變成 rejected
p 的狀態(tài)變?yōu)?rejected
此時(shí)第一個(gè)狀態(tài)變?yōu)?rejected 的 promise 對(duì)象的拒因作為 p 的拒因
let p1 = Promise.resolve(1);
let p2 = Promise.resolve(2);
let p3 = 3;
Promise.all([p1,p2,p3]).then((data)=>{
console.log(data); // print: [1,2,3]
})
let p1 = Promise.resolve(1);
let p2 = new Promise((resolve,reject)=>{
setTimeout(function(){
reject('p2 error')
},1000)
})
let p3 = new Promise((resolve,reject)=>{
setTimeout(function(){
reject('p3 error')
},500)
})
Promise.all([p1,p2,p3]).catch((error)=>{
console.log(error); // print: p3 error
})
Promise.race
- Promise.race 方法同樣用于將多個(gè) promise 對(duì)象包裝成一個(gè)新的 promise 對(duì)象
const p = Promise.race([p1, p2, p3]);
- p1、p2优训、p3 都是 promise 對(duì)象揣非,如果不是,調(diào)用 Promise.resolve 方法轉(zhuǎn)換為 promise 對(duì)象
- p 的狀態(tài)由 p1忌傻、p2水孩、p3 中狀態(tài)最先變?yōu)?fulfilled 或 rejected 的 promise 對(duì)象決定
- p 的終值或拒因由最先變更狀態(tài)的 promise 對(duì)象所決定
let p1 = Promise.resolve(1);
let p2 = new Promise((resolve,reject)=>{
setTimeout(function(){
reject('p2 error')
},1000)
})
let p3 = new Promise((resolve,reject)=>{
setTimeout(function(){
reject('p3 error')
},500)
})
Promise.race([p1,p2,p3]).then(data=>{
console.log(data);
}).catch(error=>{
console.log(error);
})
// print: 1
let p1 = new Promise((resolve,reject)=>{
setTimeout(function(){
resolve(1)
},1000)
})
let p2 = new Promise((resolve,reject)=>{
setTimeout(function(){
reject('p2 error')
},800)
})
let p3 = new Promise((resolve,reject)=>{
setTimeout(function(){
reject('p3 error')
},500)
})
Promise.race([p1,p2,p3]).then(data=>{
console.log(data);
}).catch(error=>{
console.log(error);
})
// print: p3 error
Promise 的錯(cuò)誤捕獲
當(dāng) promise 的狀態(tài)為 rejected 且為對(duì) promise 對(duì)象使用 catch 方法俘种,此時(shí)的異常信息會(huì)被 promise 對(duì)象吃掉 可以通過(guò)監(jiān)聽(tīng) unhandledRejection 事件绝淡,專門(mén)監(jiān)聽(tīng)未捕獲的reject錯(cuò)誤
// node 環(huán)境下
process.on('unhandledRejection', error => {
console.log('unhandledRejection', error);
});
// 瀏覽器下
window.addEventListener('unhandledrejection',(e)=>{
e.preventDefault();
console.log(e);
});
Promise 的問(wèn)題
- 無(wú)法取消Promise牢酵,若沒(méi)有狀態(tài)變更馍乙,也無(wú)法停止 promise 的等待
- 不設(shè)定 then 或 catch 方法垫释,構(gòu)造函數(shù)(excutor函數(shù))錯(cuò)誤饶号,無(wú)法捕獲
- 未完成狀態(tài)時(shí)季蚂,無(wú)法得知是剛開(kāi)始扭屁,還是即將完成