屬於,async是es6的新特性,用來表示程式裡面可能有非同步過程。用async關鍵字聲明的函數返回的是一個Promise對象,如果在函數中return一個直接量,async會把這個直接量通過Promise.resolve()封裝成Promise對象;當async函數沒有返回值時,返回“ Promise.resolve(undefined)」。
本教學操作環境: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) //同理 //这样子写的代码更加的美观,并且更符合我们平时编写代码的习惯
【相关推荐:javascript视频教程、编程视频】
以上是async屬於es6屬性嗎的詳細內容。更多資訊請關注PHP中文網其他相關文章!