async函數的回傳值為 promise 對象,promise物件的結果由async函數執行的傳回值決定。 async函數能使得非同步操作變得更方便,簡而言之就是 Generator 的語法糖。
定義async函數,特點是即便函數內部回傳結果不是promise物件,呼叫函數其最後的回傳結果還是promise對象,程式碼如下:
如果傳回的結果不是Promise 物件的情況下:
<script> async function fn(){ // 返回的结果是字符串 // return '123' // // 返回的结果是undefined // return; // 返回的结果是抛出一个异常 throw new 'error' } const result = fn() console.log(result); </script>
如果傳回的結果是Promise 對象時,我們正常使用then 方法即可,如下:
<script> async function fn(){ return new Promise((resolve,reject)=>{ // resolve('成功的数据') reject('失败的数据') }) } const result = fn() // 调用 then 方法 result.then((value)=>{ console.log(value); },(reason)=>{ console.log(reason); // 打印失败的数据 }) </script>
透過上文的對async 介紹,感覺其功能有點雞肋,其實恰恰不是,而是async 需要搭配await 一起使用才能達到文法糖的效果。
await的特點:
#await必須寫在async 函數中
await右邊的表達式一般為promise 物件
await回傳的是promise 成功的值
await的promise 失敗了,就會拋出異常,需要透過try...catch捕獲處理
#說白了:await就相當於then 方法的第一個回呼函數,只回傳成功的值,失敗的值需要try...catch來捕獲。
async函數內部拋出錯誤,會導致傳回的 Promise 物件變成reject狀態。拋出的錯誤物件會被catch方法回呼函數接收到。
<script> const p = new Promise((resolve,reject)=>{ // resolve('用户数据') reject('用户加载数据失败了') }) async function fn(){ // 为防止promise是失败的状态,加上try...catch进行异常捕获 try { // await 返回的结果就是 promise 返回成功的值 let result = await p console.log(result); } catch (error) { console.log(error);//因为是失败的状态,所以打印:用户加载数据失败了 } } fn() </script>
總結:
#(1)await指令後面的Promise對象,運行結果可能是rejected,所以最好把await指令放在try...catch程式碼區塊中。
(2)如果有多個await指令後面的非同步操作,如果不存在繼發關係,最好讓它們同時觸發。
例如:await Promise.all([a(), b()]),這裡簡單提一下
(3)await指令只能用在async函數之中,如果用在普通函數,就會報錯。
(4)(理解async的運行原理) async 函數可以保留運行堆疊,而普通函數內部運行一個非同步任務時,如果非同步任務運行結束普通函數可能早就運行完了,非同步任務的上下文環境已經消失了,如果非同步任務報錯,錯誤堆疊將不包括普通函數;而async函數內部的非同步任務運行時,async函數是暫停執行的,所以一旦async函數內部的非同步任務運行報錯,錯誤堆疊將包含async函數。
// 函数声明 async function foo() {} // 函数表达式 const foo = async function () {}; // 对象的方法 let obj = { async foo() {} }; obj.foo().then(...) // Class 的方法 class Storage { constructor() { this.cachePromise = caches.open('avatars'); } async getAvatar(name) { const cache = await this.cachePromise; return cache.match(`/avatars/${name}.jpg`); } } const storage = new Storage(); storage.getAvatar('jake').then(…); // 箭头函数 const foo = async () => {};
和先前講解的promise 讀取檔案內容一樣,我們也可以使用async進行檔案的讀取,程式碼如下:
// 1.引入 fs 模块 const fs = require('fs') // 2.读取文件 function index(){ return new Promise((resolve,reject)=>{ fs.readFile('./index.md',(err,data)=>{ // 如果失败 if(err) reject(err) // 如果成功 resolve(data) }) }) } function index1(){ return new Promise((resolve,reject)=>{ fs.readFile('./index1.md',(err,data)=>{ // 如果失败 if(err) reject(err) // 如果成功 resolve(data) }) }) } function index2(){ return new Promise((resolve,reject)=>{ fs.readFile('./index2.md',(err,data)=>{ // 如果失败 if(err) reject(err) // 如果成功 resolve(data) }) }) } // 3.声明一个 async 函数 async function fn(){ let i = await index() let i1 = await index1() let i2 = await index2() console.log(i.toString()); console.log(i1.toString()); console.log(i2.toString()); } fn()
和之前講解promise發送ajax請求 一樣,我們也可以使用async進行發送ajax請求,程式碼如下:
<script> // 发送 AJAX请求,返回的结果是 Promise 对象 function sendAjax(url){ return new Promise((resolve,reject)=>{ // 创建对象 const x = new XMLHttpRequest() // 初始化 x.open('GET',url) // 发送 x.send() // 事件绑定 x.onreadystatechange = function(){ if(x.readyState === 4){ if(x.status >= 200 && x.status < 300){ // 如果响应成功 resolve(x.response) // 如果响应失败 reject(x.status) } } } }) } // promise then 方法测试 // const result = sendAjax("https://ai.baidu.com/").then(value=>{ // console.log(value); // },reason=>{}) // async 与 await 测试 async function fn(){ // 发送 AJAX 请求 let result = await sendAjax("https://ai.baidu.com/") console.log(result); } fn() </script>
生成器講解 ;一比較就發現: async函數就是將Generator 函數的星號(*)替換成async,將yield替換成await。程式碼比較如下:
<script> // Generator 函数 function * person() { console.log('hello world'); yield '第一分隔线' console.log('hello world 1'); yield '第二分隔线' console.log('hello world 2'); yield '第三分隔线' } let iterator = person() // console.log(iterator); 打印的就是一个迭代器对象,里面有一个 next() 方法,我们借助next方法让它运行 iterator.next() iterator.next() iterator.next() // async函数 const person1 = async function (){ console.log('hello world'); await '第一分隔线' console.log('hello world 1'); await '第二分隔线' console.log('hello world 2'); await '第三分隔线' } person1() </script>async函數的實作原理就是將 Generator 函數和自動執行器包裝在一個函數裡。
<script> async function fn(args) {} // 等同于 function fn(args) { // spawn函数就是自动执行器 return spawn(function* () {}); } </script>我們可以分析 Generator 和 async 程式碼的書寫特點和風格:
<script> // Generator 函数 function Generator(a, b) { return spawn(function*() { let r = null; try { for(let k of b) { r = yield k(a); } } catch(e) { /* 忽略错误,继续执行 */ } return r; }); } // async 函数 async function async(a, b) { let r = null; try { for(let k of b) { r = await k(a); } } catch(e) { /* 忽略错误,继续执行 */ } return r; } </script>
所以 async 函数的实现符合语义也很简洁,不用写Generator的自动执行器,改在语言底层提供,因此代码量少。
从上文代码我们可以总结以下几点:
(1)Generator函数执行需要借助执行器,而async函数自带执行器,即async不需要像生成器一样需要借助 next 方法才能执行,而是会自动执行。
(2)相比于生成器函数,我们可以看到 async 函数的语义更加清晰
(3)上面就说了,async函数可以接受Promise或者其他原始类型,而生成器函数yield命令后面只能是Promise对象或者Thunk函数。
(4)async函数返回值只能是Promise对象,而生成器函数返回值是 Iterator 对象
【推荐学习:javascript高级教程】
以上是深入探討JavaScript中的async函數的詳細內容。更多資訊請關注PHP中文網其他相關文章!