promise的含義
promise是一個(gè)異步編程的解決方法,簡單來說,promise類似一個(gè)容器,里面保存著未來某個(gè)時(shí)間點(diǎn)才會(huì)結(jié)束的事件。
new Promise((resolve,reject)=>{
if(成功)){
resolve()
} else {
reject()
}
})
而捕抓錯(cuò)誤的寫法是:
promise.then(function(data){
// success
})
.catch(function(err){
// err
})
promise有三種狀態(tài)(并且狀態(tài)是不可逆的):
- pending:進(jìn)行中
- fulfilled: 已成功
- rejected: 已經(jīng)失敗
狀態(tài)改變,只能從padding變成fulfilled或者rejected
async函數(shù)的實(shí)現(xiàn)原理
async 函數(shù)返回一個(gè)promise對象,當(dāng)函數(shù)執(zhí)行的時(shí)候,一旦遇到await就會(huì)先返回,等到觸發(fā)的異步操作完成后再繼續(xù)執(zhí)行后面的語句

async / await 的使用,只需要在function外加速async 然后再函數(shù)內(nèi)部異步執(zhí)行的函數(shù)添加await
Generator 函數(shù)
Generator函數(shù)是es6提供的一種異步編程的解決方案,語法行為與傳統(tǒng)函數(shù)完全不一樣。Generator函數(shù)最大的特點(diǎn)就是交出函數(shù)的執(zhí)行權(quán),并且可以多次返回,每次返回值作為迭代器的一部分保存下來,可以被我顯示調(diào)用
從語法上,我們可以把他想象成一個(gè)狀態(tài)機(jī),封裝了很多個(gè)內(nèi)部狀態(tài)。
執(zhí)行Generator函數(shù)會(huì)返回一個(gè)遍歷對象,也就是Generator函數(shù)除了是狀態(tài)機(jī)還是一個(gè)遍歷對象生成函數(shù)。返回遍歷對象,可以依次遍歷Generator函數(shù)內(nèi)部的每一個(gè)狀態(tài)。
形式上Generator函數(shù)是一個(gè)普通函數(shù),但有兩個(gè)特征。
function關(guān)鍵字與函數(shù)名之間有個(gè)
*
函數(shù)體內(nèi)使用yield表達(dá)式,定義不同的內(nèi)部狀態(tài)
而且當(dāng)你直接調(diào)用一個(gè)Generator函數(shù),它不會(huì)直接執(zhí)行,返回的也不是函數(shù)運(yùn)行結(jié)果,而是指向內(nèi)部應(yīng)用狀態(tài)的指針對象,就是遍歷對象
然后下一步,必須調(diào)用遍歷器對象的next方法,使得指針移向下一個(gè)狀態(tài)。也就是說,每次調(diào)用next方法,內(nèi)部指針就從函數(shù)頭部,或者上次的地方執(zhí)行,直到遇到下一個(gè)yield表達(dá)式(或者return語句為止)
所以你可以把yield當(dāng)成一個(gè)暫停標(biāo)記,而next方法可以恢復(fù)執(zhí)行
而訪問每一個(gè)狀態(tài)就需要調(diào)用next。每次next會(huì)自動(dòng)執(zhí)行到下一個(gè)yield上,直到return
function* gen(){
yield "hello";
yield "world";
return "ends"
}
let g1=gen() // gen{<suspended>}
console.log(g1.next()); // {value:'hello',done:false}
console.log(g1.next()) // {value:"world",done:false}
console.log(g1.next()) // {value:"ends",done:true}
console.log(g1.next()) // {value:undefined,done:true}
console.log(g1.next()) // {value:undefined,done:true}
Generator函數(shù)的實(shí)例。它具有狀態(tài)值suspended和closed,suspended代表暫停,closed則為結(jié)束
一旦執(zhí)行過return之后,,以后再調(diào)用這個(gè)方法返回的也是這個(gè)值
總結(jié):
調(diào)用Generator函數(shù),返回的都是一個(gè)遍歷對象,代表Generator函數(shù)內(nèi)部的指針,之后的每次調(diào)Generator的next方法,就會(huì)返回一個(gè)有著value和done兩個(gè)屬性。value屬性表示當(dāng)前內(nèi)部的值,也就是yield的后面的值。 done屬性的值,是一個(gè)布爾值,表示遍歷是否已經(jīng)結(jié)束。
yield
yield表達(dá)式
由于Generator函數(shù)返回的是遍歷對象,而要想繼續(xù)執(zhí)行則需要調(diào)用next方法才能執(zhí)行下一個(gè)內(nèi)部的狀態(tài),所以實(shí)際提供了一個(gè)可以暫停的函數(shù)函數(shù)就是yield
關(guān)于next的運(yùn)行邏輯如下:
- 遇到y(tǒng)ield表達(dá)式,就暫停執(zhí)行后面的操作,并且將yield后面的值作為對象的value
- 下一次調(diào)用next方法的時(shí)候,再繼續(xù)往下執(zhí)行,直到遇到下一個(gè)yield表達(dá)式
- 如果沒有遇到下一個(gè)yield,則會(huì)直接運(yùn)行到函數(shù)結(jié)束,直到return為止,并且返回return的值
- 如果沒有return,則返回的value中為undefined
yield的存在,相當(dāng)于為javascript提供了惰性求值的語法功能。
如果Generator函數(shù)內(nèi)部不使用yieldb表達(dá)式,這時(shí)就變成了一個(gè)單純的暫緩執(zhí)行函數(shù)
function* gen(){
console.log("執(zhí)行了!")
}
let g1=gen()
console.log(g1); // gen {<suspended>}
像上述代碼,如果你想執(zhí)行,則需要使用next函數(shù)才能執(zhí)行
function* gen(){
console.log("執(zhí)行了!"); // 執(zhí)行了!
}
let g1=gen()
console.log(g1.next()); // {value:undefined,done:true}
而且yield只能只用在Generator函數(shù)中,使用在普通函數(shù)中會(huì)報(bào)錯(cuò)。
另外,yield表達(dá)式如果在另一個(gè)表達(dá)式中,必須放上圓括號(hào)
function* demo() {
console.log('Hello' + yield); // SyntaxError
console.log('Hello' + yield 123); // SyntaxError
console.log('Hello' + (yield)); // OK
console.log('Hello' + (yield 123)); // OK
}
yield 委托*
在Generator函數(shù)中有時(shí)候,會(huì)需要將多個(gè)迭代器的值合在一起。我們可以用yield*形式,將執(zhí)行委托給另外一個(gè)Generator函數(shù)
function* foo1() {
yield 1;
yield 2;
return "foo1 end";
}
function* foo2() {
yield 3;
yield 4;
}
function* foo() {
yield* foo1();
yield* foo2();
yield 5;
}
const iterator = foo();
console.log(iterator.next()); // "{ value: 1, done: false }"
console.log(iterator.next()); // "{ value: 2, done: false }"
console.log(iterator.next()); // "{ value: 3, done: false }"
console.log(iterator.next()); // "{ value: 4, done: false }"
console.log(iterator.next()); // "{ value: 5, done: false }"
console.log(iterator.next()); // "{ value: undefined, done: true }"
我們可以看到,并沒有執(zhí)行foo1中的return語句,那是因?yàn)?,在整個(gè)Generator函數(shù)只能有一次return,所有的yield*在Generator函數(shù)都是以函數(shù)表達(dá)式的形式出現(xiàn)的。return的值是表達(dá)式的結(jié)果,在委托結(jié)束之前其內(nèi)部都是停止的,等待到表達(dá)式的結(jié)束的時(shí)候,直接返回給foo,但是此時(shí)foo沒有接收這個(gè)變量所以,并未打印
如圖就是接收foo1中return值的寫法

next方法傳參
yield本身沒有返回值,它總是會(huì)返回undefined。next方法可以攜帶一個(gè)參數(shù),該參數(shù)會(huì)被當(dāng)作yield表達(dá)式的值
如下圖:

同理如果next不攜帶參數(shù),它返回的就是undefined
由于Generator函數(shù)從暫停狀態(tài)到恢復(fù)運(yùn)行,它的上下文(context)是不變的,通過next傳參這個(gè)功能可以讓Generator函數(shù)在不同階段的運(yùn)行階段,從外部向內(nèi)部注入值,從而調(diào)整函數(shù)的行為
throw方法
throw根據(jù)函數(shù)中書寫try catch返回catch中的內(nèi)容,如果沒有寫try則直接拋出異常

可以看到一旦我們拋出了錯(cuò)誤,那么Generator的狀態(tài)直接就會(huì)改變成true。而且value會(huì)變成undefined。
需要注意的是,如果我們執(zhí)行throw()的時(shí)候Generator函數(shù)里面沒有try Catch則會(huì)直接報(bào)錯(cuò)。如下
function* foo(x) {
yield x + 1
try {
yield x + 2
} catch (e) {
console.log('catch it')
}
}
const result = foo(0) // foo {<suspended>}
result.next(); // {value: 1, done: false}
result.throw(); // Uncaught undefined
result.next(); //{value: undefined, done: true}
上面的錯(cuò)誤是因?yàn)?,此時(shí)的語句還沒執(zhí)行到try里面。
iterator 迭代器
任意一個(gè)對象的Symbol.iterator方法,等于該對象的遍歷器生成函數(shù),調(diào)用該函數(shù)會(huì)返回對象的一個(gè)遍歷器對象
而由于Generator函數(shù)就是遍歷器生成函數(shù),因此可以把Generator賦值給對象的Symbol.iterator屬性,從而使得該對象具有Iterator接口

可以看到嘗試使用for...of遍歷object時(shí),會(huì)報(bào)錯(cuò)obj is not iterable對象不能被遍歷
下面我們嘗試使用iterator接口

如果對象是key:value類型,控制臺(tái)也能打印出相對應(yīng)的value

實(shí)現(xiàn) Async await

在執(zhí)行過程中,判斷一個(gè)函數(shù)的promise是否已經(jīng)執(zhí)行完(因?yàn)閜romise完成會(huì)return值,return代表這個(gè)yield表達(dá)式以及完成),如果完成直接返回值,沒有則繼續(xù)重復(fù)這個(gè)步驟
總結(jié)
通過理解了Generator函數(shù),再看async/await的實(shí)現(xiàn)是比較簡單的。因?yàn)槠鋵?shí)就是利用Generator函數(shù)的能夠暫停的特性,和通過yield能夠獲取表達(dá)式的執(zhí)行狀態(tài),從而去判斷promise的執(zhí)行是否已經(jīng)完成。
參考資料:http://www.itdecent.cn/p/f703d3f54ebd
參考資料:http://www.itdecent.cn/p/0f1b6ae1888c
參考資料: https://mp.weixin.qq.com/s/gT9MF1l52HuKUpmeNIByCQ