一、異步函數(shù)與同步函數(shù)的操作順序
例子1:異步爸爸和異步兒子、同步兒子的故事
async function myAsync(){ timer= await setInterval(()=>{ console.log(count) count++; if(count>10){clearInterval(timer)} console.log("我是異步") },100) console.log("我是同步") }
myAsync()
if和console.log()是屬于同一級別的,但是因為if里面有異步函數(shù),所以比同步慢執(zhí)行;
例子2:異步爸爸和異步兒子,與同步兄弟的故事
async function myAsync(){ timer= await setInterval(()=>{ console.log(count) count++; if(count>10){clearInterval(timer)} console.log("我是異步") },100) console.log("我是同步1") } myAsync() console.log("我是同步2")
console.log("同步2")與myAsync是同一級,但是myAsync是異步函數(shù),所以先輸出同步2,又因為例子1說明的原因,所以再輸出同步1,最后執(zhí)行我是異步。
例子3:同步爸爸與異步兒子,與同步兄弟的故事
function myAsync(){ timer= setInterval(()=>{ console.log(count) count++; if(count>10){clearInterval(timer)} console.log("我是異步") },100) console.log("我是同步1") } myAsync() console.log("我是同步2")
此時由于myAsync與console.log()都是同一級別的,所以遵循從上到下執(zhí)行,又因為myAsync的兒子是異步,所以myAsync的兒子最慢執(zhí)行。
例子4:
async function myAsync1(){ timer= setInterval(()=>{ console.log(count) count++; if(count>10){clearInterval(timer)} console.log("我是異步1") },100) } async function myAsync2(){ setTimeout(()=>{ console.log("我是異步2") },100) } myAsync1() myAsync2() console.log("我是同步2")
同樣,同步最先執(zhí)行,兩個異步都是同級,按時間觸發(fā)順序,執(zhí)行
把例子4的兩個異步調一下個:
async function myAsync1(){ timer= setInterval(()=>{ console.log(count) count++; if(count>10){clearInterval(timer)} console.log("我是異步1") },100) } async function myAsync2(){ setTimeout(()=>{ console.log("我是異步2") },100) } myAsync2() myAsync1() console.log("我是同步2")
根據(jù)上面幾個例子說明:
(1)同一級別下,同步異步執(zhí)行順序都是同步函數(shù)最先執(zhí)行,異步函數(shù)最慢執(zhí)行
(2)而兩個一樣的異步函數(shù),按照順序,上面的先執(zhí)行,下面的慢執(zhí)行
(3)兩個不一樣的異步函數(shù),要比較異步函數(shù)內部是否有繼續(xù)嵌套異步,還是嵌套同步,有同步的先執(zhí)行;
(4)兩個一樣的異步執(zhí)行函數(shù),但是時間觸發(fā)不一樣的,按照時間觸發(fā)先后,按順序執(zhí)行,這也就是實際開發(fā)中,你并不知道你向服務器索取的資源哪一個先拿到。先拿到資源的,就先執(zhí)行,但是這個時間是未知的。
------------------------------------------------------------------------------------------------------------------------------------------開始講些有用的:
寫個例子:
let arr1 = [] function myAsync(){ setTimeout(()=>{ return arr1.push(1);//對數(shù)組賦值 },1000) } let num = myAsync(); console.log(num);
運行結果:
答案很明顯,賦值時由于異步函數(shù)setIterval還沒操作,就賦值了,明顯賦了個寂寞,所以undefined;
那么早期的時候,我們是怎么拿到異步函數(shù)里面的值的呢?
二、早期與異步函數(shù)的愛恨情仇:
利用回調函數(shù)!看下面這個例子:
let arr1 = [] function myAsync(callback){ setTimeout(()=>{ return callback(arr1.push(1));//對數(shù)組賦值 },1000) }
傳入一個參數(shù)callback,這個參數(shù)其實就是一個函數(shù),那么怎么取值?
// 箭頭函數(shù)寫法 myAsync((arg)=>{ console.log(arg);//arg為參數(shù),其實就是拿到了arr1.push里面的內容 }) //es5寫法 myAsync(function callback(arg){ console.log(arg) })
執(zhí)行順序分析:首先執(zhí)行myAsync(),執(zhí)行完畢后回調callback(),開始執(zhí)行,這就是所說的回調函數(shù),說白了就是先執(zhí)行myAsync(),接著執(zhí)行callback,如何callback內部把內容輸出。所以拿到值的其實是callback幫忙拿到的。
有了上面這個思路,所以我們的前輩們很開心,找到了探索新世界的大門,用之而來請求網(wǎng)絡數(shù)據(jù)時非常開心,因為可以拿到異步操作里面的值了!
然而開心了沒多久,開始難過了,此時出現(xiàn)了一個怪物:
他叫回調地獄,為什么會有它呢,其實實際應用場景需要先完成第一個異步任務,才能執(zhí)行第二個異步任務,接著才能完成第三個異步任務,具體例子我現(xiàn)在還沒想到,知道的可以評論一下,因為我是個菜雞。
接著你看一下下面這個例子:
async function myAsync1(callback){ setTimeout(()=>{ callback(arr1.push("任務1,我需要最先完成")) },1000) } async function myAsync2(callback){ setTimeout(()=>{ callback(arr1.push("任務2,我需要第二完成")) },1000) } async function myAsync3(callback){ setTimeout(()=>{ callback(arr1.push("任務3,我需要最慢完成")) },1000) } function totalTasks(){ myAsync1(cb1=>{ // 若cb1非空,就是cb1初始化了,可以操作cb1了,專業(yè)點說法就是它加載入內存了,,才能繼續(xù)下面執(zhí)行cb2,,下面依次類推 if(cb1){ console.log(arr1) myAsync2(cb2=>{ if(cb2){ console.log(arr1) myAsync3(cb3=>{ if(cb3){ console.log(arr1); } }) } }) } }) } totalTasks()
結果:
這還是僅僅三層回調,那么要是多層回調,再寫成網(wǎng)絡請求格式的,你會寫到一半自己線繞暈了。
所以前輩們寫這個的時候表情是這樣子的:
想拉屎,但是又拉不出來的感覺。
三、promise救世主的出現(xiàn)
愚笨而又可愛的人類啊,我來拯救你們啦:
此時我們只要這樣寫就好了:
async function myAsync(){ return await new Promise(res=>{ setTimeout(()=>{ arr1.push(1) res(arr1) },100) }) } let p = myAsync() p.then((data)=>{ console.log(data) })
promise呢,其實也是用的回調的思想,但是他這種語法格式我們用起來理解就簡單多了,維護起來也方便。
它執(zhí)行的原理就是回調,他其實有兩個參數(shù),第一個參數(shù)是resolve,表示成功取到值的操作,第二個,表示請求被拒絕的操作,這兩個參數(shù)也都是回調函數(shù)。
下面看個例子,講一下里面的各個參數(shù)的作用:
new Promise( function (resolve, reject) { // 一段耗時的異步操作 resolve('成功') // 數(shù)據(jù)處理完成 // reject('失敗') // 數(shù)據(jù)處理出錯 } ).then( (res) => {console.log(res)}, // 成功 (err) => {console.log(err)} // 失敗 )
resolve(參數(shù)),如果數(shù)據(jù)請求成功,它會把參數(shù)發(fā)給then,以備進行下一步操作,reject(參數(shù)),如果數(shù)據(jù)請求錯誤,他會把參數(shù)傳給then以備下一步操作。
而then函數(shù)它其實也是一個promise函數(shù),它的兩個參數(shù)作用也一樣,then函數(shù)可以傳給下一個then函數(shù),子子孫孫無窮盡也。
形如:
new Promise( function (resolve, reject) { // 一段耗時的異步操作 resolve('成功') // 數(shù)據(jù)處理完成 reject('失敗') // 數(shù)據(jù)處理出錯 } ).then( (res) => {console.log(res)}, // 成功 (err) => {console.log(err)} // 失敗 ).then( (res) => {console.log(res)}, // 成功 (err) => {console.log(err)} // 失敗 ).then( (res) => {console.log(res)}, // 成功 (err) => {console.log(err)} // 失敗 ).then( (res) => {console.log(res)}, // 成功 (err) => {console.log(err)} // 失敗 )
其實還有一個catch函數(shù),它的作用相當于then函數(shù)的第二個參數(shù),也相當于promise的第二個參數(shù),都是用來處理錯誤操作的:
new Promise( function (resolve, reject) { // 一段耗時的異步操作 resolve('成功') // 數(shù)據(jù)處理完成 // reject('失敗') // 數(shù)據(jù)處理出錯 } ).then( (res) => {console.log(res)}, // 成功 (err) => {console.log(err)} // 失敗 ).catch{ console.log(err) }
具體為什么多次一舉,加個catch,因為前面的then函數(shù)已經可以處理錯誤操作了,網(wǎng)上說還是可能異步引起的錯誤,有些錯誤只有catch才能發(fā)現(xiàn),這個等我變成大神后再說下原因。
四、promise,與async 與await實現(xiàn)異步操作按順序執(zhí)行:
// 2.await async 與promise async function myAsync1(){ return await new Promise(res=>{ setTimeout(()=>{ console.log("任務1") res("任務1") },1000) }) } async function myAsync2(){ return await new Promise(res=>{ setTimeout(()=>{ console.log("任務2") res("任務2") },10) }) } async function myAsync3(){ return await new Promise(res=>{ setTimeout(()=>{ console.log("任務3") res("任務3") },1) }) } async function totalTask(){ await myAsync1(); await myAsync2(); await myAsync3(); } totalTask()
直接用async 與await實現(xiàn)異步函數(shù)順序執(zhí)行。
注:async與await關系密切,只有聲明了async的函數(shù),里面才能使用await。await作用就是等待這個函數(shù)執(zhí)行,等待執(zhí)行完再執(zhí)行下一個。
好了,寫到這里了,我好帥?。?/p>
聯(lián)系客服