天天看點

建議收藏!async ,await相關知識總結,适用于js基礎不錯的童鞋

早在很久之前就知道async和awiat的大名了,但我卻一直對這個東西也算是不溫不火吧;

最近在搞koa的時候 大量出現的 async和await把我搞得有點煩了;

我就單獨抽出來 半天時間 好好來解決下整個async和await

可能有的童鞋知道 他是基于es6的promise基礎上進行擴充了;

在講解之前我們就來談談 promise的由來的曆史吧; 雖然有點廢話了 但助你了解async的還是有點幫助的.

1.promise的曆史

可能會碰見 這樣的面試 會什麼會有 promise這個東西,他的存在是為了解決什麼的

很多童鞋 可能都會說出來 是來解決回調地獄的問題 那麼問題來了 能詳細談一談什麼是回調地獄呢;

當然這個問題 對于一些很厲害的 童鞋也不算什麼問題,

這裡我要說一個他的另一個由來的原因

在很久很久之前的

我們在一個函數内部 如果有異步函數的話 怎麼擷取到 異步函數裡面的變量呢

可能手快的童鞋 就直接在異步函數中進行了 return 但是真的能拿到嗎?

function test(){
	setTimeout(()=>{
		return "hello world"
	},1000)
}
console.log(text())  // undefined 
           

上面的原因是由于 函數内部 立即執行完畢 并不會等待 異步函數的執行完畢 在同步那塊什麼都沒有傳回東西 列印當然是 undefined

那我們怎麼拿到 異步裡面執行的變量呢

// 以前的程式員的解決方法就是 使用回調函數 

function test(callback){
	setTimeout(()=>{
		callback("hello world")
	},1000)
}
test(function(msg){console.log(msg);});
           

上面的程式其實可以看成下面的這種情況 

function test(callback){
	setTimeout(()=>{
		callback("hello world")
	},1000)
}
var callback=function(msg){
	console.log(msg)
}
test(callback)
           

但問題是 如果工作中也想這麼簡單就好了 前端的同行都知道 經常要和伺服器進行ajax交流

但是交流就交流吧 還經常 下一個ajax交流的參數依賴上一個ajax交流的結果

這就形成了 嵌套 問題是有的時候套一層還不夠 還得套好幾層 這就産生了回調地獄的問題了

這個時候promise就誕生了

我們這裡使用promise将上面的程式進行修改下

function test(){
	return new Promise((resolve,reject)=>{
		setTimeout(()=>{
			resolve("hello world");
		},1000)
	})
	
}
test().then(res=>{console.log(res)}) // 輸出 hello world
           

是不是看起來就有那麼一絲感覺了是吧

函數體内部傳回一個new promise函數

在函數執行後 有一個.then方法 來接收 執行成功的 内部 

我們可以把變量放到 resolve() 裡面作為參數 在 .then中用來接收

但是 promise 我就不在這裡細講了 可以去看看阮一峰老師的 es6

2.async 和await

在學習async和await 之前 不要把它當做一個新知識來說

他不過是我們之前學過的東西 穿上了 一層新的衣服 首先内心上就不夠對他産生恐懼

官方解釋一大堆專業名詞 ,那就不要去看了 ,越看越迷惑

通俗的講解就是 就是将 你的異步代碼 看起來更像是 同步 不要将它想的太高大上

我依次舉幾個例子 你們可能就懂了

1.函數前面加上 async 之後 函數傳回一個 promise對象

async function test(){
	console.log(111);
}
console.log(test());
           
建議收藏!async ,await相關知識總結,适用于js基礎不錯的童鞋

 如果函數裡面沒有 傳回資料的話 就是 在promise 後面就是一個undefined

2.帶傳回值的函數

async function test(){
	return "111";
}
console.log(test());
           
建議收藏!async ,await相關知識總結,适用于js基礎不錯的童鞋

 那我們怎麼拿到那個傳回值呢

聰明的童鞋可能就想到了 既然是promise對象 那就按照promise的那種方式拿呗

bingo clever!

async function test(){
	return "111";
}
test().then(res=>{console.log(res)}); // 輸出 111
           

 3.在沒有 await 關鍵字之前 不要把sync 當做異步看待 他裡面的執行體 隻要不是異步代碼 和同步帶嗎一視同仁

按照正常順序走就是了

async function test(){
	console.log(111);
}
test();
console.log(2222);
           
建議收藏!async ,await相關知識總結,适用于js基礎不錯的童鞋

4.await 關鍵字

記住了await必須隻能在  前面有async 的函數體内 否則報錯

這裡先舉一個正常點的例子吧

這裡可以先暫停下 知道點await的童鞋可以先去标注下下面的輸出順序

function func(){
	return new Promise((resolve)=>{
		setTimeout(()=>{
			console.log(111);
			resolve('222');
		},3000)
	})
}
async function test(){
	var n=await func();
	console.log(n);
	 console.log(333);
}
test();
console.log(444);
// 答案 : 1.444  2.111  3.222   4.333 
           

這個await 要關鍵進行講解了

await 後面 要等待的函數 對象有兩種1,傳回promise 2.不傳回promise的

這兩者之前下面又分為 傳回的是同步還是異步 有沒有傳回值 下面都進行講解

有一個很重要的一點就是 不管上面得是那種情況 await 都會阻塞它下面代碼的執行

4.1 先說下 不傳回promise的函數對象吧 且是同步的代碼 沒有傳回值

// await 會阻塞 下面的代碼執行 先執行函數外面的 同步 代碼

是以下面的輸出順序是 111 333 222

function func(){
	console.log(111);
}
async function test(){
	await func();
	console.log(222);
}
test();
console.log(333);
           
建議收藏!async ,await相關知識總結,适用于js基礎不錯的童鞋

4.2 await 不傳回promise對象的函數對象吧 且是同步的代碼 有傳回值的

await 是代碼從右向左執行的 左邊的函數執行完之後 有傳回值複制給 n

下面代碼的執行順序是 333 111 222 

function func(){
	return "111";
}
async function test(){
	var n=await func();
	console.log(n);
	console.log(222);
}
test();
console.log(333);

           

4.3 await 不傳回promise對象的函數對象吧 且是異步的代碼 沒有傳回值的

還是要記住 await 會阻塞後面代碼的執行 先執行 外邊的同步代碼

下面函數的執行順序是 111 444 333 222

function func(){
	console.log(111);
	setTimeout(()=>{
		console.log(222);
	},1000)
}
async function test(){
	await func();
	console.log(333);
}
test();
console.log(444);
           

這裡可能有的童鞋納悶了 不是阻塞後面代碼的執行 先執行 完await 後面的函數麼

這裡确實 執行完了 看見那個 111 是先列印的

但是 後面有一個異步函數 它自然要進入 異步的事件隊列裡面 等待 定時 事件走完 他就要執行了

是以 這下 自然 人家後面的 333 就要執行了

awati 是讓代碼看起來像是同步 的 并不是 代碼走的也變成同步阻塞了哈

4.4 await 不傳回promise對象的函數對象吧 且是異步的代碼 有傳回值的(迷惑人呢)

下面的輸出 自然就是 // 111 444 undefined 333

func 執行完了 沒有傳回值 自然是對的 就是undefined了

那個說人家異步函數裡面有傳回值的 那個func函數都執行完了 這個值 是拿不到外邊

要想拿到 就看下面的promise吧

function func(){
	console.log(111);
	setTimeout(()=>{
		return "222";
	},1000)
}
async function test(){
	var n=await func();
	console.log(n);
	console.log(333);
}
test();
console.log(444);
           

4.5 await 傳回promise對象的函數對象吧 且不是異步的代碼 沒有傳回值的

這裡輸出的順序就是 111 333

這裡可能納悶 為啥子 沒有 222

要記牢 如果 傳回的是promise對象的話 沒有傳回值

await 等不到 傳回值的話 下面的代碼他就不執行了 記住這點 哦 童鞋們

function func(){
	return new Promise((resolve,reject)=>{
		console.log(111);
	})
}
async function test(){
	await func();
	console.log(222);
}
test();
console.log(333);
           

 4.6 await 傳回promise對象的函數對象吧 且是異步的代碼 沒有傳回值的

// 下面代碼的輸出 順序 是 333 111

同樣沒有收到傳回值 直接不執行下面的 console.log(222)了

function func(){
	return new Promise((resolve,reject)=>{
		setTimeout(()=>{
			console.log(111);
		},100)
	})
}
async function test(){
	await func();
	console.log(222);
}
test();
console.log(333);
           

 4.7 await 傳回promise對象的函數對象吧 且不是異步的代碼 有傳回值的

// 下面的執行順序是 111 444 222 333

function func(){
	return new Promise((resolve,reject)=>{
		console.log(111);
		resolve(222);
	})
}
async function test(){
	var n=await func();
	console.log(n);
	console.log(333);
}
test();
console.log(444);
           

4.8 await 傳回promise對象的函數對象吧 且是異步的代碼 有傳回值的

下面代碼的執行順序是 111 444 222 333

await 知道傳回的是promise 它要拿到 傳回的值 指派給 n 是以就是列印出來這個順序了

function func(){
	return new Promise((resolve,reject)=>{
		console.log(111);
		setTimeout(()=>{
			resolve(222);
		},1000)
	})
}
async function test(){
	var n=await func();
	console.log(n);
	console.log(333);
}
test();
console.log(444);
           

這下 應該就明白

最後 附上一到async 和await的經典面試題

async function async1(){
    console.log('async1 start') 
    await async2()
    console.log('async1 end')  
}
async function async2(){
    console.log('async2')  
}
console.log('script start'); 
setTimeout(function(){
    console.log('setTimeout')
},0)  
async1();
new Promise(function(resolve){
    console.log('promise1') 
    resolve();
}).then(function(){
    console.log('promise2') 
})
console.log('script end')
           

繼續閱讀