Promise
1.異步行為是為了優(yōu)化因計(jì)算量大而時(shí)間長(zhǎng)的操作.
2.pedding 待定: 表示尚未開始或正在進(jìn)行中
? ?fulfilled 解決: 表示已經(jīng)成功完成
? ?rejected 拒絕: 表示沒有完成
3.從pedding狀態(tài)切換到fulfilled狀態(tài)或rejected狀態(tài)后,狀態(tài)就不會(huì)再改變.而且也不能保證promise比如會(huì)脫離待定狀態(tài).
因此,無論promise是resolve還是reject,甚至永遠(yuǎn)處于待定狀態(tài),都應(yīng)該具有恰當(dāng)?shù)男袨?/p>
4.執(zhí)行器函數(shù)的兩項(xiàng)職責(zé): 初始化promise的異步行為和控制狀態(tài)的最終轉(zhuǎn)換.
控制promise狀態(tài)的轉(zhuǎn)換是通過resolve()和reject()來實(shí)現(xiàn)的
resolve()會(huì)把狀態(tài)切換為兌現(xiàn)
reject()會(huì)把狀態(tài)切換為拒絕,reject()會(huì)拋出錯(cuò)誤.
5. 執(zhí)行器函數(shù)是同步執(zhí)行的, 因?yàn)?span style="color:#fe2c24;">執(zhí)行器函數(shù)是promise的初始化程序
console.log(1);
new Promise((resolve,reject) => {
console.log(3);
})
console.log(2);
//打印1, 3, 2
6.無論resolve()和reject()中的哪個(gè)被調(diào)用,狀態(tài)轉(zhuǎn)換后都不可撤銷. 繼續(xù)修改狀態(tài)會(huì)默認(rèn)失敗
const p = new Promise((resolve,reject) => {
resolve('666')
reject('000') // 沒有效果
})
console.log(p); // 666
7.為了避免promise卡在待定狀態(tài),可以添加一個(gè)定時(shí)退出功能,通過setTimeout設(shè)置一個(gè)10秒后無論如何都會(huì)拒絕promise的回調(diào)
如果執(zhí)行器中的代碼在超時(shí)之前已經(jīng)解決或拒絕,那么再次調(diào)用reject也會(huì)默認(rèn)失敗
8.Promise.resolve()
????????通過Promise.resolve()靜態(tài)方法,可以實(shí)例化一個(gè)解決的Promise
????????使用這個(gè)靜態(tài)方法實(shí)際上可以把任何值都轉(zhuǎn)換為一個(gè)Promise
const p1 = new Promise((resolve,reject) => resolve())
const p2 = Promise.resolve() // undefined
const p3 = Promise.resolve(3) // 3
const p4 = Promise.resolve(4,5) // 4 多余參數(shù)會(huì)忽略, 可以使用對(duì)象
9.Promise.reject()
實(shí)例化一個(gè)拒絕的promise并拋出一個(gè)異步錯(cuò)誤(這個(gè)錯(cuò)誤不能通過try/catch捕獲,只能通過拒絕處理程序捕獲)
const p = Promise.reject(4) // promise <rejected> 4
10. Promise的實(shí)例方法
10.1.實(shí)現(xiàn)Thenable接口
10.2.Promise.prototype.then()
????????Promise.prototype.then()是為Promise實(shí)例添加處理程序的主要方法.
????????then()最多接收兩個(gè)參數(shù) onResolved處理程序和onRejected處理程序,這兩個(gè)參數(shù)都是可選的分別進(jìn)入兌現(xiàn)和拒絕狀態(tài)
let p1 = new Promise((resolve,reject) => setTimeout(resolve,2000))
let p2 = new Promise((resolve,reject) => setTimeout(reject,2000))
p1.then(() => onResolved('p1'),
() => onRejected('p1'))
p2.then(() => onResolved('p2'),
() => onRejected('p2'))
function onRejected(id){
console.log(id,'rejected');
}
function onResolved(id){
console.log(id,'onResolved');
}
因?yàn)閜romise的只能轉(zhuǎn)換最終狀態(tài)一次,所以這兩個(gè)操作一定的互斥的.
傳給then()的任何非函數(shù)類型的參數(shù)都會(huì)被靜默忽略.如果只提供onRejected參數(shù),要在onResolved位置上傳入undefined
p1.then('glsdfosdfisdjif') // 會(huì)被忽略,不推薦
p1.then(null,() => onRejected('p2')) // 不傳onResolved的規(guī)范寫法
Promise.prototype.then()返回一個(gè)新的Promise實(shí)例, 會(huì)通過Promise.resolve()隱式包裝來生成新的Promise
const p3 = Promise.resolve('wei') // 默認(rèn)返回undefined
10.3.Promise.prototype.catch()
Promise.prototype.catch()是為Promise實(shí)例添加拒絕處理程序.
實(shí)際上這個(gè)方法就是一個(gè)語法糖,相當(dāng)于調(diào)用了 Promise.prototype.then(null,onRejected)
返回一個(gè)新的Promise實(shí)例,會(huì)通過Promise.resolve()隱式包裝來生成新的Promise
10.4.Promise.prototype.finally()
????????Promise.prototype.finally()用于給promise添加onFinally處理程序,在promise轉(zhuǎn)換為解決或者拒絕都會(huì)執(zhí)行, 主要是避免在resolve和rejected中出現(xiàn)冗余代碼. 但是沒辦法知道promise的狀態(tài)是解決還是拒絕.
返回一個(gè)新的Promise實(shí)例,不同于then()和catch()方法返回的實(shí)例.在大多數(shù)情況下它將表現(xiàn)為父Promise的傳遞.
10.5.非重入Promise方法
當(dāng)Promise進(jìn)入落定狀態(tài)時(shí),與該狀態(tài)相關(guān)的處理程序僅僅會(huì)被排期,而非立即執(zhí)行.(就是被放到一個(gè)微任務(wù)里去了)
// 例子
const p = Promise.resolve()
p.then(() => {
console.log('p.then()');
})
console.log('同步執(zhí)行');
// 實(shí)際輸出 同步執(zhí)行, p.then()
// 例子
let sy;
let p = new Promise((resolve) => {
sy = function() {
console.log(1);
resolve()
console.log(2);
}
})
p.then(() => {
console.log(4);
})
sy()
console.log(3);
/*
實(shí)際輸出: 1,2,3,4 即使Promise的狀態(tài)發(fā)生在添加處理程序之后,
處理程序也會(huì)等到運(yùn)行的消息隊(duì)列讓出時(shí),才會(huì)執(zhí)行.
*/
10.6.臨近處理程序的執(zhí)行順序
10.7.傳遞解決值和拒絕理由
????????解決的值和拒絕的理由分別通過resolve()和reject()的第一個(gè)參數(shù)往后傳,直到報(bào)錯(cuò)
????????Promise.resolve() 和Promise.reject() 在被調(diào)用時(shí)就會(huì)接受解決值和拒絕理由.
10.8.拒絕Promise與拒絕錯(cuò)誤處理
????????拒絕Promise類似于throw()表達(dá)式.它們都代表一種程序狀態(tài), 即需要中斷或者特殊處理
let p1 = new Promise((resolve,reject) => reject( Error('1')))
let p2 = new Promise((resolve,reject) => { throw Error('2') })
let p3 = Promise.resolve().then(() => { throw Error('3') })
let p4 = Promise.reject(() => Error('4'))
console.log(p1);
console.log(p2);
console.log(p3);
console.log(p4);
Promise.resolve().then()的錯(cuò)誤最后才出現(xiàn).
正常情況下,在通過throw()關(guān)鍵字拋出錯(cuò)誤時(shí),js運(yùn)行時(shí)的錯(cuò)誤處理機(jī)制會(huì)停止執(zhí)行拋出錯(cuò)誤之后的任何代碼(拋出錯(cuò)誤之后不解析了)
// 正常情況xxx不會(huì)執(zhí)行
throw Error()
console.log('xxx');
// promise拋出錯(cuò)誤時(shí),因?yàn)殄e(cuò)誤實(shí)際上是從消息隊(duì)列中拋出來的,所有不會(huì)阻止運(yùn)行時(shí)
Promise.reject(Error('x'))
console.log('xxx'); // 會(huì)輸出
// 例子:
let p = new Promise((resolve,reject) => {
console.log(1);
reject(Error())
}).then(() => {
console.log(2);
}).catch((e) => {
console.log(3);
}).catch(() => {
console.log(4);
}).then(() => {
console.log(5);
})
/*
實(shí)際輸出1,3,5 首先打印1, 沒毛病, 接下來執(zhí)行reject(),打印3, catch 函數(shù)會(huì)隱式的調(diào)用
Promise.resolve()函數(shù), 因此繼續(xù).then()打印5
*/
10.9.Promise連鎖與合成
多個(gè)Promise組合在一起可以構(gòu)成強(qiáng)大的代碼邏輯.
通過兩種方式實(shí)現(xiàn): promise連鎖和promise合成,前者是一個(gè)promise接一個(gè)promise的拼接,后者則是將多個(gè)promise組合成一個(gè)promise
//要執(zhí)行真正的異步任務(wù),讓每個(gè)執(zhí)行器都返回一個(gè)promise實(shí)例
let p1 = new Promise((resolve,reject) => {
console.log(1);
setTimeout(resolve,1000)
})
p1.then(() => new Promise((resolve,reject) => {
console.log(2);
setTimeout(resolve,1000)
}))
.then(() => new Promise((resolve,reject) => {
console.log(3);
setTimeout(resolve,1000)
}))
.then(() => new Promise((resolve,reject) => {
console.log(4);
setTimeout(resolve,1000)
}))
// 1 1秒后
// 2 2秒后
// 3 3秒后
// 4 4秒后
? ?
????????Promise的處理程序是按照它們添加的順序執(zhí)行的,由于Promise的處理程序是先添加到消息隊(duì)列, 然后才逐個(gè)執(zhí)行.
將多個(gè)Promise實(shí)例組合成一個(gè)Promise的靜態(tài)方法. Promise.all()和Promise.race()
Promise.all()方法創(chuàng)建的promise會(huì)將一組promise全部解決后,再返回結(jié)果.
Promise.all()接收一個(gè)可迭代對(duì)象,返回一個(gè)新Promise
let p1 = Promise.all([
Promise.resolve(),
Promise.resolve()
])
// 可迭代對(duì)象中的元素會(huì)通過Promise.resolve()轉(zhuǎn)換為Promise
let p2 = Promise.all([3,4])
// 空的可迭代對(duì)象等價(jià)于Promise.resolve()
let p2 = Promise.all([])
// 無效的語法
let p2 = Promise.all() // 報(bào)錯(cuò)
有一個(gè)Promise待定,則合成的Promise也會(huì)待定, 有一個(gè)Promise拒絕,則合成的Promise也會(huì)拒絕.
// 一次拒絕導(dǎo)致最終Promise的拒絕
let p1 = Promise.all([
Promise.resolve(),
Promise.resolve(),
Promise.reject()
])
// 如果所有的promise都成功解決,則合成promise的解決值就是所有包含promise解決值的數(shù)組(按迭代器順序)
let p1 = Promise.all([
Promise.resolve(1),
Promise.resolve(2),
Promise.resolve()
])
p1.then(res => {
console.log(res); [1, 2, undefined]
})
// 多個(gè)reject的情況
let p1 = Promise.all([
Promise.reject(1),
Promise.reject(2),
Promise.resolve()
])
p1.then(res => {
console.log(res);
/*
1 會(huì)將第一個(gè)拒絕的理由作為合成promise的拒絕理由. 之后再拒絕的promise的不會(huì)
影響最終promise的拒絕理由.
*/
})
Promise.race()方法創(chuàng)建的任意一個(gè) Promise 對(duì)象狀態(tài)變?yōu)?fulfilled 或 rejected 時(shí)立即返回該 Promise 對(duì)象的值或原因。
語法同all()
只要第一個(gè)落定的promise,Promise.race()就會(huì)包裝其解決值或姐拒絕理由并返回新promise
ES6不支持取消Promise和進(jìn)度通知,一個(gè)主要原因就是這樣會(huì)導(dǎo)致promise連鎖和promise合成過渡復(fù)雜化.
async await
功能: 讓以同步方式寫的代碼能夠異步執(zhí)行.
async/await解決利用異步結(jié)構(gòu)組織代碼的問題.
1. async 關(guān)鍵字用于聲明異步函數(shù) 可以用在函數(shù)聲明,函數(shù)表達(dá)式,箭頭函數(shù)和方法上
async function foo() {}
let bar = async function() {}
let baz = async () => {}
class Qux {
async qux(){}
}
使用async關(guān)鍵字可以讓函數(shù)具有異步特征,但總體上代碼仍然是同步求值的.
而在參數(shù)或閉包方面,異步函數(shù)仍然具有普通函數(shù)js函數(shù)的正常行為.
例子:
async function foo() {
console.log('1');
}
foo()
console.log(2);
// 1,2
異步函數(shù)如果使用return 關(guān)鍵字返回了值(沒有return 返回undefined),這個(gè)值會(huì)被Promise.resolve()包裝成一個(gè)Promise對(duì)象.
async function foo() {
console.log('1');
// return 666
return Promise.resolve(666) // 效果同上
}
// 給返回的promise添加一個(gè)解決處理程序
foo().then(console.log)
// 1, 666
在異步函數(shù)中拋出錯(cuò)誤會(huì)返回拒絕的Promise
async function foo() {
console.log('1');
throw 3
console.log('4'); // 不執(zhí)行
}
// 給返回的promise添加一個(gè)解決處理程序
foo().catch(console.log)
console.log(2);
// 1, 2, 3
2. 異步函數(shù)主要針對(duì)不會(huì)馬上完成的任務(wù),需要一種暫停和恢復(fù)執(zhí)行的能力.
使用await關(guān)鍵字可以暫停異步函數(shù)代碼的執(zhí)行,等待Promise的解決.它可以單獨(dú)使用,也可以在表達(dá)式中使用
await命令后面是一個(gè)Promise對(duì)象. 如果不是,會(huì)被轉(zhuǎn)成一個(gè)立即resolve的Promise對(duì)象.
// 對(duì)拒絕promise使用await會(huì)釋放錯(cuò)誤值(將拒絕promise返回)
async function foo() {
console.log('1');
await Promise.reject(3)
// return await Promise.resolve(3)
// resolve需要return then才能接受, 而reject不需要return catch也可以接收
console.log('4'); // 這行代碼不會(huì)執(zhí)行
}
// 給返回的promise添加一個(gè)解決處理程序
foo().then(console.log).catch(console.log)
console.log(2);
// await Promise.reject(3) 打印輸出1,2,3
只要一個(gè)await語句后面的Promise變成reject, 那么整個(gè)async函數(shù)都會(huì)中斷執(zhí)行.
// 下面await語句是不會(huì)執(zhí)行的,因?yàn)榈谝粋€(gè)await已經(jīng)變成了reject
async function foo() {
await Promise.reject('出錯(cuò)了')
await Promise.resolve('hello') // 不會(huì)打印
}
如果希望前一個(gè)異步操作失敗,也不要中斷后面的異步操作.
這時(shí)可以將第一個(gè)await放在try...catch 這也不管這個(gè)異步是否成功,第二個(gè)await都會(huì)執(zhí)行
async function foo() {
try {
await Promise.reject('出錯(cuò)了') // 會(huì)打印
} catch (e) {
console.log(e);
}
return await Promise.resolve('hello') // 會(huì)打印
}
foo().then(v => console.log(v))
另一種方式是在await后面的promise對(duì)象后添加一個(gè)catch方法,處理前面可能出現(xiàn)的錯(cuò)誤
async function foo() {
await Promise.reject('出錯(cuò)了').catch (e => console.log(e))//會(huì)打印
return await Promise.resolve('hello') // 會(huì)打印
}
foo().then(v => console.log(v))
3.await 的限制
await關(guān)鍵字必須在異步函數(shù)中使用.
不允許:await出現(xiàn)在箭頭函數(shù)中
不允許:await出現(xiàn)在同步函數(shù)聲明中
不允許:await出現(xiàn)在同步函數(shù)表達(dá)式中
不允許:IIFE使用同步函數(shù)表達(dá)式或箭頭函數(shù)
停止和恢復(fù)執(zhí)行
使用await關(guān)鍵字之后的區(qū)別其實(shí)看上去還要微妙一些.
async function foo() {
console.log(await Promise.resolve('foo'));
}
async function bar() {
console.log(await 'bar');
}
async function baz() {
console.log('baz');
}
foo()
bar()
baz()
// 打印 baz, bar, foo
async/await中真正起作用的是await. async只是一個(gè)標(biāo)識(shí)符,畢竟異步函數(shù)如果不包含await關(guān)鍵字,基本和普通函數(shù)沒什么區(qū)別
async function foo() {
console.log(2);
}
console.log(1);
foo()
console.log(3);
// 打印 1, 2, 3
要完全理解await關(guān)鍵字,必須知道它并非只是等待一個(gè)值可用那么簡(jiǎn)單.
js運(yùn)行時(shí)在碰到await關(guān)鍵字時(shí), 會(huì)記錄在哪里暫停執(zhí)行. 等到await后面的值可用了,js運(yùn)行時(shí)會(huì)向消息隊(duì)列中推送一個(gè)任務(wù),這個(gè)任務(wù)會(huì)恢復(fù)異步函數(shù)的執(zhí)行
因此, 即使await后面跟著一個(gè)立即可用的值,函數(shù)的其余部分也會(huì)被異步求值.下面例子:
async function foo() {
console.log(2);
await null
console.log(4);
}
console.log(1);
foo()
console.log(3);
// 打印 1, 2, 3, 4
/*
運(yùn)行時(shí)的工作過程:
(1) 打印1;
(2) 調(diào)用異步函數(shù)foo();
(3) (在foo中)打印2;
(4) (在foo中)await關(guān)鍵字暫停執(zhí)行,為立即可用的值null向消息隊(duì)列中添加一個(gè)任務(wù);
(5) foo()退出;
(6) 打印3;
(7) 同步線程執(zhí)行完畢;
(8) js運(yùn)行時(shí)從消息隊(duì)列中取出任務(wù),恢復(fù)異步函數(shù)執(zhí)行;
(9) (在foo中)恢復(fù)執(zhí)行,await取得null值(這里并沒有用)
(10) (在foo中)打印4;
(11) foo()返回
*/
?
// TC39對(duì)await后面是promise的情況如何處理做過一次修改. 修改后await Promise.resolve(8)只會(huì)生成一個(gè)異步任務(wù)
async function foo() {
console.log(2);
console.log(await Promise.resolve(8));
console.log(9);
}
async function bar() {
console.log(4);
console.log(await 6);
console.log(7);
}
console.log(1);
foo()
console.log(3);
bar()
console.log(5);
// 打印 1, 2, 3, 4, 5, 8, 9, 6, 7
使用注意點(diǎn):
?1.await命令后面的promise對(duì)象的運(yùn)行結(jié)果可能是rejected,最好把a(bǔ)wait放在try...catch代碼塊中
?2.多個(gè)await命令后面的異步操作如果不存在繼發(fā)關(guān)系,最好讓他們同時(shí)觸發(fā)
? ? ? ?
// getFoo和getBar是兩個(gè)獨(dú)立的異步操作,被寫成激發(fā)關(guān)系,這樣比較耗時(shí).可以讓它們同時(shí)觸發(fā)
let foo = await getFoo()
let bar = await getBar()
let [foo, bar] = await Promise.all([getFoo,getBar])
async函數(shù)的實(shí)現(xiàn)原理:
async函數(shù)的實(shí)現(xiàn)原理就是將Generator函數(shù)和自動(dòng)執(zhí)行器包裝在一個(gè)函數(shù)里.
面試官: 來說一下你對(duì)Promise的理解?
個(gè)人對(duì)Promise的理解是, promise是一種異步編程的解決方案, 它比傳統(tǒng)的回調(diào)函數(shù)加事件更加合理合強(qiáng)大,
目前除了使用promise的異步操作外,還使用promise在項(xiàng)目中解決回調(diào)地獄等問題.
promise是一個(gè)對(duì)象可以獲取異步操作的信息
promise的特點(diǎn):
?????????對(duì)象不受外界影響,promise一共有三個(gè)狀態(tài), 分別是進(jìn)行中,成功和失敗,只有異步操作的結(jié)果,可以決定是哪一種狀態(tài),任何其他的操作都無法改變這個(gè)狀態(tài).
????????一旦狀態(tài)改變就不會(huì)在變,任何時(shí)候都可以得到這個(gè)結(jié)果,promise的狀態(tài)改變只有兩種可能要么成功要么失敗
如果要使用promise必須對(duì)promise進(jìn)行實(shí)例化, 實(shí)例化之后promise內(nèi)有一個(gè)回調(diào)函數(shù),這個(gè)函數(shù)有兩個(gè)參數(shù),分別是resolve和reject,
當(dāng)我們的狀態(tài)發(fā)生變化的時(shí)候,如果是成功則會(huì)通過resolve將成功的結(jié)果返回,如果失敗通過reject將錯(cuò)誤的信息返回出去.
通過.then方法接收成功的結(jié)果,通過.catch方法接收失敗的結(jié)果
promise常用的方法還用promise.all(),race()方法. 主要是將多個(gè)實(shí)例包裝成一個(gè)新的實(shí)例.
Promise.all()方法創(chuàng)建的promise會(huì)將一組promise全部解決后,再返回結(jié)果.
Promise.race()方法哪個(gè)接口跑的快,先返回哪個(gè)文章來源:http://www.zghlxwxcb.cn/news/detail-436671.html
在項(xiàng)目中一般使用promise來對(duì)接口進(jìn)行封裝,以及一些異步的操作都會(huì)用到promise.校驗(yàn)文章來源地址http://www.zghlxwxcb.cn/news/detail-436671.html
到了這里,關(guān)于徹底理解Promise和async/await的文章就介紹完了。如果您還想了解更多內(nèi)容,請(qǐng)?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!