上一篇文章寫了異步中的promise(強烈推薦看
),這一篇文章準備寫一下異步中的generator和async函數,下一篇文章準備寫js中的運行機制,也就是eventloop和宏任務微任務,這樣異步三兄弟就齊齊整整了。每篇文章後面都會配套相應的大廠面試題,你們必定不要錯過。面試
Generator 函數是 ES6 提供的一種異步編程解決方案,語法行爲與傳統函數徹底不一樣。
Generator 函數是一個狀態機,封裝了多個內部狀態。yield至關於隔斷點
執行 Generator 函數會返回一個遍歷器對象,經過調用next方法使狀態不斷改變next
方法能夠帶一個參數,該參數就會被看成上一個yield
表達式的返回值。編程
//readFile是我本身封裝的一個讀取文件的方法,返回Promise function * read() { let age = yield readFile('./name.txt','utf8'); let agedata = yield readFile(`./${age}`,'utf8'); return agedata }
上面read是一個generator函數,segmentfault
let it = read()
it是一個遍歷器對象,函數調用可是裏面代碼尚未執行let {value,done} = it.next()
第一次next函數開始執行知道遇到yield,此時返回一個對象,vlaue是yield後面的這個promise,done是falsevalue.then((data)=>{let {value,done} = it.next(data)})
等第一個文件讀取完,我再next一下,傳入讀取的結果,這個結果會做爲上一個yield的返回值賦值給a,並執行第二個yield的語句
此時返回值agedata爲{讀第二個文件的promise,false}
`value.then((data)=>{promise
console.log(data) //打印第二個文件的內容 it.next() //函數結束,返回一個{value:第二個文件的內容,done:true}
})`瀏覽器
generator是解決異步的另一個方案,可是咱們須要手動調用next方法,總感受挺麻煩的,tj大神寫了一個co庫,代碼就幾行,能幫咱們自動調用next方法,解決這generator的這個缺陷異步
co(it){ return new Promise((resolve,reject)=>{ function next(val){ {value,done}=it.next(val) //手動next程序到第一個yield的地方 if(done){ //都已經成功了還有什麼好說的,直接返回結果就好了 resolve(value) }else{ return Promise.resolve(value).then(next,rejext) //核心,看懂co的思想就會了 } } next(undefined) }) }
重點講解一下第八行,首先強烈建議看個人上篇文章promise
value是第一個yiled的返回值,多是個同步,多是個settimeout,多是個Promise,因此不論是什麼,我先給它包裝成promise,讓他有then方法,而後我在then方法裏面遞歸調用next,若是後面有yield再進next方法裏就會走else自動調next,這樣一個自動執行器就造成了
,若是沒有就resolve就好了。須要特別注意的是Promise.resolve(value).then(next)這個寫法,next實際上是有參數的,就是value的異步結果
async
咱們再用generator函數就能夠這麼用了異步編程
//read是章節2中的generator函數 co(read()).then((data)=>{ console.log(data) })
是否是超級簡單函數
async、awiat實際上就是generator+co函數的語法糖,實際也能夠說就是generator+promise實現的,就是generator+自執行器
oop
function * read() { let age = yield readFile('./name.txt','utf8'); let agedata = yield readFile(`./${age}`,'utf8'); return agedata } ------------------------------------------------------- async read(){ let age = await readFile('./name.txt','utf8'); let agedata = await readFile(`./${age}`,'utf8'); return agedata }
從上面能夠看出是否是就是*換成async,yield換成await?
可是咱們用同步寫法寫async 可使用try catch
原本這章我以爲應該放在下篇文章裏面講的,不過,既然剛講了async await的原理,咱就打鐵趁熱吧
若是你有eventloop和微任務宏任務的相關知識,那麼接着往下看,不然請移步到個人下篇文章
先講一下執行順序
promise.then 是一個微任務,因此瀏覽器裏它確定不是settimeout實現的,上一篇文章只是寫了個符合A+規範的實現
async read(){ console.log(1) let age = await readFile('./name.txt','utf8'); let agedata = await readFile(`./${age}`,'utf8'); return agedata }
read執行時,是否是先回默認調一下next,因此第一個await前的都是同步代碼,而後第一個await後面的是否是會包裝成一個promise而後調用then方法呀?then了之後調用next後面的代碼纔會執行。因此第一個await後面的代碼能夠當作再promise.then裏面的,至關因而一個微任務,因此程序在主棧裏面執行async函數,會先執行await前面的宏任務,而後遇到await,await後面的會先放到微任務隊列裏面,返回主棧去執行主棧裏面的其餘宏任務
比較官方的說法:async 函數返回一個 Promise 對象,當函數執行的時候,一旦遇到 await 就會先返回,等到觸發的異步操做完成,再執行函數體內後面的語句。能夠理解爲,是讓出了線程,跳出了 async 函數體。
說一些比較可能的狀況
若是等待一下時間能成功,那麼一段時間會繼續日後執行,緣由和以前的同樣
若是是一個一段時間後失敗的Promise呢,then後面會走reject,而co函數裏面reject中可沒有調用next方法哦
因此函數就掛了後面的代碼不會執行,會默認拋錯
可是咱們用同步寫法寫async await的時候是能夠用try catch的
setTimeout、Promise、Async/Await 的區別?
JS 異步解決方案的發展歷程以及優缺點?
答案:
回調函數
缺點:回調地獄 沒有返回值
promise
優勢:解決回調地獄,提供的一些方法仍是挺好用的
缺點:仍是要寫大量回調函數,挺麻煩的;錯誤必須捕獲(不捕獲反應不到外面)
generaotr
缺點:須要寫next,或者說缺一個自執行器
async await
優勢 能夠用try catch 捕獲異常,將異步代碼改爲同步代碼
缺點 若是幾個await沒有依賴性,會有性能問題,這個啥意思呢,
好比讀文件1後根據文件1的內容才能讀文件2 這個是有依賴的吧,我用async挺好的,利用next來控制 第一個文件讀2秒,第二個文件讀2秒共四秒
可是若是文件1文件2沒有關係,我明明能夠同時開啓兩個文件的讀取呀,用await就會阻塞,這也是我剛說的promise有些方法仍是很好用的,好比這裏可使用的promise.all
Async/Await 如何經過同步的方式實現異步
答案;解釋下generator+手寫一個co函數