屬於,async是es6的新特性,用於表明程式裡面可能有非同步過程。用async關鍵字宣告的函數返回的是一個Promise物件,如果在函數中return一個直接量,async會把這個直接量通過Promise.resolve()封裝成Promise物件;當async函數沒有返回值時,返回「Promise.resolve(undefined)」。
前端(vue)入門到精通課程:進入學習
Apipost = Postman + Swagger + Mock + Jmeter 超好用的API偵錯工具:
本教學操作環境:windows7系統、ECMAScript 6版、Dell G3電腦。
我們先從字面意思來理解這兩個關鍵字,async是asynchronous(非同步)的簡寫,而await可以認為是async wait的簡寫。所以async可以理解為用於宣告一個函數是非同步的,而await用於等待一個非同步任務執行完成。
async和await關鍵字讓我們可以用一種更簡潔的方式寫出基於promise的非同步行為,而無需刻意地鏈式呼叫promise。
接下來我們通過先幾個例子,初步瞭解一下async和await的作用。
知識點1: 用 async 關鍵字宣告的函數返回的是一個 Promise 物件。如果在函數中 return
一個直接量,async 會把這個直接量通過 Promise.resolve()
封裝成 Promise 物件。當 async
函數沒有返回值時,返回 Promise.resolve(undefined)
//定義一個普通函數,返回一個字串
function test() {
return "hello async";
}
const result1 = test();
console.log(result1); //輸出一個字串 hello async
//定義一個使用了async修飾的函數,同樣返回一個字串
async function testAsync() {
return "hello async";
}
const result2 = testAsync();
console.log(result2); //輸出一個Promise物件 Promise {<fulfilled>: 'hello async'}
登入後複製
//async較好的用法
async function testAsync(){
//返回一個Promise物件
return new Promise((resolve, reject)=>{
//處理非同步任務
setTimeout(function () {
resolve("testAsync")
}, 1000);
})
}
//async通常用於宣告一個處理非同步任務且返回了Promise物件的函數
登入後複製
知識點2: await關鍵字只能使用在被async宣告的函數內,用於修飾一個Promise物件,使得該Promise物件處理的非同步任務在當前協程上按順序同步執行。
//定義一個使用async修飾的函數,處理非同步任務
async function testAsync(){
return new Promise((resolve, reject)=>{
setTimeout(function () {
resolve("testAsync")
}, 1000);
})
}
登入後複製
//定義一個函數,直接呼叫testAsync函數
function testAwait(){
console.log('testAsync呼叫前')
testAsync().then(res=>{
console.log(res) //輸出"testAsync"
})
console.log('testAsync呼叫後')
}
/***** 輸出如下 *****/
testAsync呼叫前
testAsync呼叫後
testAsync
//儘管程式碼按順序寫,但不按順序執行,因為testAsync()是非同步函數
登入後複製
//定義一個函數(不使用async宣告該函數)用await修飾呼叫testAsync函數
function testAwait(){
console.log('testAsync呼叫前')
await testAsync().then(res=>{ //使用await關鍵字修飾
console.log(res)
})
console.log('testAsync呼叫後')
}
//呼叫testAwait()函數
testAwait()
//報錯:Uncaught SyntaxError: await is only valid in async functions and the top level bodies of modules,因為await只能使用在被async修飾的函數內。
登入後複製
//定義一個函數(使用async宣告該函數)用await修飾呼叫testAsync函數
async function testAwait(){
console.log('testAsync呼叫前')
await testAsync().then(res=>{
console.log(res)
})
console.log('testAsync呼叫後')
}
/***** 輸出如下 *****/
testAsync呼叫前
testAsync
testAsync呼叫後
//使用了await關鍵字修飾,使得程式碼按照順序執行,即同步執行
登入後複製
(1)用於表明程式裡面可能有非同步過程
(2)async函數返回值的型別為Promise物件: 這是和普通函數本質上不同的地方,也是使用時重點注意的地方;
(3)無等待,非阻塞:使用async關鍵字宣告的函數裡面如果有非同步過程可能會等待,但是函數本身會馬上返回,不會阻塞當前主執行緒。如果在函數裡面使用了await關鍵字修飾的非同步過程,其工作在相應的協程上,會阻塞等待非同步任務的完成再返回。
//定義一個函數,處理非同步任務(使用定時器模擬),返回一個Promise物件
async function testAsync(){
return new Promise((resolve, reject) => {
setTimeout(function () {
resolve("成功呼叫testAsync")
}, 1000);
});
}
//定義一個函數,使用await關鍵字修飾呼叫testAsync()函數
async function testAwait(){
//使用了await關鍵字修飾呼叫testAsyn()函數
await this.testAsync().then(res=>{
console.log(res) //輸出的是testAsync()函數resolve的值
});
console.log("helloAsync");
}
//主執行緒
console.log('testAwait呼叫前')
testAwait();
console.log('testAwait呼叫後')
/***** 輸出結果如下 *****/
testAwait呼叫前
testAwait呼叫後 //因為testAwait()函數使用了async關鍵字修飾,所以不會阻塞主執行緒的執行,所以這兩句話會先直接輸出,然後再執行testAwait()函數
成功呼叫testAsync //因為testAwait()函數在內部呼叫testAsync()函數時使用了await關鍵字修飾,所以在對應的協程上會阻塞,等待testAsync()函數執行完,再輸出下面那句'helloAsync'
helloAsync
登入後複製
(1)await只能在async函數內部使用:不能放在普通函數裡面,否則會報錯。
(2)await關鍵字後面跟的是一個Promise物件。如果跟的是一個函數,則這個函數應當返回一個Promise物件。如果跟的是非Promise物件,則會通過Promise.resolve( )函數自動將這個東西包裝成一個Promise物件並置於fulfilled狀態。
//例如:
const a = await 'Hello Await'
// 相當於
const a = await Promise.resolve('Hello Await');
console.log(a) //輸出 'Hello Await'
登入後複製
(3)await的本質是等待它所修飾的Promise物件的fulfilled狀態,並把resolve(data)的資料data返回。
意思是,如果await後面跟的是一個 Promise
物件,await
就會阻塞後面的程式碼,等著 Promise
物件 resolve
,然後得到 resolve
的值,作為 await
表示式的運算結果。
async function testAsync(){
return new Promise((resolve, reject) => {
setTimeout(function () {
resolve("成功呼叫testAsync")
}, 1000);
});
}
const a = await testAsync() //這裡的a就會拿到testAsync函數resolve的資料
console.log(a) //在一秒後輸出'成功呼叫testAsync'
登入後複製
(4)await並不關心它所修飾的Promise物件的rejected狀態,即reject(data)的資料data並不會被await處理,所以建議通過Promise物件呼叫catch去捕獲。
async testAwait(){
//變數a用於接收testAsync()函數resolve的資料
let a = await testAsync().catch(err=>{
//處理異常和reject的資料
})
}
登入後複製
(1)執行順序
//定義一個函數,該函數接收一個引數,1s後再返回引數的兩倍
async function double(num) {
return new Promise((resolve, reject) => {
setTimeout(() => { //使用定時器模擬非同步任務
resolve(2 * num) //將運算結果交給resolve
}, 1000);
})
}
async function getResult () {
console.log('double呼叫前') //順序:2
let result = await double(10); //將10作為引數傳遞給double函數
//result變數用於接收double()函數resolve的值
console.log(result); //順序:4
console.log('double呼叫後') //順序:4
}
console.log('getResult呼叫前') //順序:1
getResult();
console.log('getResult呼叫後') //順序:3
/***** 依次輸出如下 *****/
getResult呼叫前
double呼叫前
getResult呼叫後
20 //1s後輸出
double呼叫後
登入後複製
①首先列印輸出getResult呼叫前
,同步程式碼,順序執行;
②然後呼叫方法getResult( ),列印輸出double呼叫前
,同步程式碼,順序執行;
③再呼叫非同步方法double( )
如果此處沒有使用await關鍵字修飾,則依次輸出的是:getResult呼叫前、double呼叫前、double呼叫後、getResult呼叫後、1s後輸出20
因為非同步操作不會影響其他程式碼的執行,所以會將其他程式碼按順序執行完,最後再執行double函數
因為這裡使用了await關鍵字,所以getResult( )的程式碼執行到這裡就會被阻塞,等到double函數resolve了,再往下執行
④儘管getResult函數內部被await阻塞了,由於getResult函數本身也是個async函數,所以它不會影響getResult函數外面的程式碼執行。因為呼叫async函數不會造成阻塞,它內部的所有阻塞都被封裝在一個Promise物件中非同步執行。
⑤所以在呼叫getResult函數後,會繼續向下執行,即列印輸出getResult呼叫後
⑥當1s之後,非同步函數double執行完成,將結果交給resolve。
⑦通過await關鍵字接收到double函數resolve的值,賦值給result變數。列印輸出20
⑧因為使用了await阻塞將非同步變為同步,所以在列印輸出20後再列印輸出double呼叫後
(2)處理reject回撥
//方法一:通過promise物件的catch進行捕獲
function a(){
return new Promise((resolve,reject) => {
setTimeout(() => {
reject("something")
}, 1000)
})
}
async function b(){
let r = await a().catch((err)=>{
console.log(err)
})
}
登入後複製
//方法二:通過try/catch語句處理
function a(){
return new Promise((resolve,reject) => {
setTimeout(() => {
reject("something")
}, 1000)
})
}
async function b(){
let r = null
try{
r = await a()
}catch(err){
console.log(err)
}
}
登入後複製
(3)使用await優化Promise物件的回撥地獄問題
在Promise章節中我們通過了Promise物件的then( )方法鏈式呼叫解決了回撥地獄問題,但看起來仍然不夠美觀,我們可以通過await優化一下,讓它看起來更符合我們平時程式碼的編寫習慣。
//原本的解決方案
//第二個請求依賴於第一個請求的返回值,第三個請求依賴於第二個請求的返回值
request1().then(function(data){
return request2(data)
}).then(function(data){
return request3(data)
})
//這裡只傳送了三次請求,程式碼看起來還不錯,雖然它已經比普通的回撥函數形式好了很多。
//那如果需要傳送五次或十次請求呢?程式碼也許會沒那麼美觀,接下來我們使用學習到的await去解決這個問題。
登入後複製
原本的要求是每個請求都依賴於上一個請求的返回值,那麼是不是得等一個請求完,才能傳送下一個請求?這時我們可以思考一下,await的作用是什麼?是不是對一個Promise物件去進行阻塞,使其狀態變為fulfilled後獲取resolve的值。這不就正是我們所需要的。
//使用await的解決方案
var res1 = await request1() //將request1的返回值賦值給res1
var res2 = await request2(res1) //將res1作為引數傳給request2,並將request2的返回值賦值給res2
var res3 = await request3(res2) //同理
//這樣子寫的程式碼更加的美觀,並且更符合我們平時編寫程式碼的習慣
登入後複製
【相關推薦:、】
以上就是async屬於es6屬性嗎的詳細內容,更多請關注TW511.COM其它相關文章!