ES6+--》熟知JS中的async函数

简介: 熟知JS中的async函数

async函数

async函数的返回值为 promise 对象,promise对象的结果由async函数执行的返回值决定。async函数能使得异步操作变得更加方便,简而言之就是 Generator 的语法糖。

定义async函数,特点是即便函数内部返回结果不是promise对象,调用函数其最后的返回结果依然是promise对象,代码如下:

如果返回的结果不是 Promise 对象的情况下:

<script>asyncfunctionfn(){
// 返回的结果是字符串// return '123'// // 返回的结果是undefined// return;// 返回的结果是抛出一个异常thrownew'error'    }
constresult=fn()
console.log(result);
</script>

图片.png

如果返回的结果是 Promise 对象时,我们正常使用 then 方法即可,如下:

<script>asyncfunctionfn(){
returnnewPromise((resolve,reject)=>{
// resolve('成功的数据')reject('失败的数据')
        })
    }
constresult=fn()
// 调用 then 方法result.then((value)=>{
console.log(value);
    },(reason)=>{
console.log(reason); // 打印失败的数据    })
</script>

await 表达式

通过上文的对 async 介绍,感觉其功能有点鸡肋,其实恰恰不是,而是 async 需要搭配 await 一起使用才能达到语法糖的效果。

await的特点

await必须写在 async 函数中

await右侧的表达式一般为 promise 对象

await返回的是 promise 成功的值

await的 promise 失败了,就会抛出异常,需要通过 try...catch捕获处理

说白了:await就相当于 then 方法的第一个回调函数,只返回成功的值,失败的值需要 try...catch来捕获。

async函数内部抛出错误,会导致返回的 Promise 对象变为reject状态。抛出的错误对象会被catch方法回调函数接收到。

<script>constp=newPromise((resolve,reject)=>{
// resolve('用户数据')reject('用户加载数据失败了')
    })
asyncfunctionfn(){
// 为防止promise是失败的状态,加上try...catch进行异常捕获try {
// await 返回的结果就是 promise 返回成功的值letresult=awaitpconsole.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使用形式

// 函数声明asyncfunctionfoo() {}
// 函数表达式constfoo=asyncfunction () {};
// 对象的方法letobj= { asyncfoo() {} };
obj.foo().then(...)
// Class 的方法classStorage {
constructor() {
this.cachePromise=caches.open('avatars');
  }
asyncgetAvatar(name) {
constcache=awaitthis.cachePromise;
returncache.match(`/avatars/${name}.jpg`);
  }
}
conststorage=newStorage();
storage.getAvatar('jake').then();
// 箭头函数constfoo=async () => {};

async读取文件

和之前讲解的 promise 读取文件内容 一样,我们也可以使用async进行文件的读取,代码如下:

// 1.引入 fs 模块constfs=require('fs')
// 2.读取文件functionindex(){
returnnewPromise((resolve,reject)=>{
fs.readFile('./index.md',(err,data)=>{
// 如果失败if(err) reject(err)
// 如果成功resolve(data)
        })
    })
}
functionindex1(){
returnnewPromise((resolve,reject)=>{
fs.readFile('./index1.md',(err,data)=>{
// 如果失败if(err) reject(err)
// 如果成功resolve(data)
        })
    })
}
functionindex2(){
returnnewPromise((resolve,reject)=>{
fs.readFile('./index2.md',(err,data)=>{
// 如果失败if(err) reject(err)
// 如果成功resolve(data)
        })
    })
}
// 3.声明一个 async 函数asyncfunctionfn(){
leti=awaitindex()
leti1=awaitindex1()
leti2=awaitindex2()
console.log(i.toString());
console.log(i1.toString());
console.log(i2.toString());
}
fn()

图片.png

async发送AJAX请求

和之前讲解 promise发送ajax请求 一样,我们也可以使用async进行发送ajax请求,代码如下:

<script>// 发送 AJAX请求,返回的结果是 Promise 对象functionsendAjax(url){
returnnewPromise((resolve,reject)=>{
// 创建对象constx=newXMLHttpRequest()
// 初始化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 测试asyncfunctionfn(){
// 发送 AJAX 请求letresult=awaitsendAjax("https://ai.baidu.com/")
console.log(result);
    }
fn()
</script>

图片.png

与生成器(Generator)相比

我们发现 async与await之间的关系 和 Generator与yield之间的关系十分类似,不熟悉Generator的朋友可以看一下我之前的文章:生成器讲解 ;一比较就发现: 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'第三分隔线'    }
letiterator=person()
// console.log(iterator); 打印的就是一个迭代器对象,里面有一个 next() 方法,我们借助next方法让它运行iterator.next()
iterator.next()
iterator.next()
// async函数constperson1=asyncfunction (){
console.log('hello world');
await'第一分隔线'console.log('hello world 1');
await'第二分隔线'console.log('hello world 2');
await'第三分隔线'    }
person1()
</script>

图片.png

async函数的实现原理就是将 Generator 函数和自动执行器包装在一个函数里。

<script>asyncfunctionfn(args) {}
// 等同于functionfn(args) {
// spawn函数就是自动执行器returnspawn(function* () {});
    }
</script>

我们可以分析一下 Generator 和 async 代码的书写特点和风格:

<script>// Generator 函数functionGenerator(a, b) {
returnspawn(function*() {
letr=null;
try {
for(letkofb) {
r=yieldk(a);
                }
            } catch(e) {
/* 忽略错误,继续执行 */            }
returnr;
        });
    }
// async 函数asyncfunctionasync(a, b) {
letr=null;
try {
for(letkofb) {
r=awaitk(a);
            }
        } catch(e) {
/* 忽略错误,继续执行 */        }
returnr;
    }
</script>

所以 async 函数的实现符合语义也很简洁,不用写Generator的自动执行器,改在语言底层提供,因此代码量少。

从上文代码我们可以总结以下几点

(1)Generator函数执行需要借助执行器,而async函数自带执行器,即async不需要像生成器一样需要借助 next 方法才能执行,而是会自动执行。

(2)相比于生成器函数,我们可以看到 async 函数的语义更加清晰

(3)上面就说了,async函数可以接受Promise或者其他原始类型,而生成器函数yield命令后面只能是Promise对象或者Thunk函数。

(4)async函数返回值只能是Promise对象,而生成器函数返回值是 Iterator 对象

相关文章
|
25天前
|
监控 JavaScript 前端开发
确定使用 `defer` 属性还是 `async` 属性来异步加载 JavaScript
【10月更文挑战第24天】选择使用 `defer` 属性还是 `async` 属性来异步加载 JavaScript 是一个需要综合考虑多个因素的决策。需要根据脚本之间的依赖关系、页面加载性能要求、脚本的功能和重要性等因素来进行权衡。在实际应用中,需要通过测试和验证来确定最适合的加载方式,以提供更好的用户体验和页面性能。
|
20天前
|
JavaScript 前端开发 Java
[JS]同事:这次就算了,下班回去赶紧补补内置函数,再犯肯定被主管骂
本文介绍了JavaScript中常用的函数和方法,包括通用函数、Global对象函数以及数组相关函数。详细列出了每个函数的参数、返回值及使用说明,并提供了示例代码。文章强调了函数的学习应结合源码和实践,适合JavaScript初学者和进阶开发者参考。
34 2
[JS]同事:这次就算了,下班回去赶紧补补内置函数,再犯肯定被主管骂
|
19天前
|
前端开发 JavaScript 开发者
除了 async/await 关键字,还有哪些方式可以在 JavaScript 中实现异步编程?
【10月更文挑战第30天】这些异步编程方式在不同的场景和需求下各有优劣,开发者可以根据具体的项目情况选择合适的方式来实现异步编程,以达到高效、可读和易于维护的代码效果。
|
19天前
|
前端开发 JavaScript 开发者
除了 Generator 函数,还有哪些 JavaScript 异步编程解决方案?
【10月更文挑战第30天】开发者可以根据具体的项目情况选择合适的方式来处理异步操作,以实现高效、可读和易于维护的代码。
|
20天前
|
JSON 前端开发 JavaScript
浅谈JavaScript中的Promise、Async和Await
【10月更文挑战第30天】Promise、Async和Await是JavaScript中强大的异步编程工具,它们各自具有独特的优势和适用场景,开发者可以根据具体的项目需求和代码风格选择合适的方式来处理异步操作,从而编写出更加高效、可读和易于维护的JavaScript代码。
22 1
|
27天前
|
存储 JavaScript 前端开发
JS的ES6知识点
【10月更文挑战第19天】这只是 ES6 的一些主要知识点,ES6 还带来了许多其他的特性和改进,这些特性使得 JavaScript 更加现代化和强大,为开发者提供了更多的便利和灵活性。
20 3
|
1月前
|
JavaScript 前端开发
JavaScript 函数语法
JavaScript 函数是使用 `function` 关键词定义的代码块,可在调用时执行特定任务。函数可以无参或带参,参数用于传递值并在函数内部使用。函数调用可在事件触发时进行,如用户点击按钮。JavaScript 对大小写敏感,函数名和关键词必须严格匹配。示例中展示了如何通过不同参数调用函数以生成不同的输出。
|
1月前
|
存储 JavaScript 前端开发
JS函数提升 变量提升
【10月更文挑战第6天】函数提升和变量提升是 JavaScript 语言的重要特性,但它们也可能带来一些困惑和潜在的问题。通过深入理解和掌握它们的原理和表现,开发者可以更好地编写和维护 JavaScript 代码,避免因不了解这些机制而导致的错误和不一致。同时,不断提高对执行上下文等相关概念的认识,将有助于提升对 JavaScript 语言的整体理解和运用能力。
|
1月前
|
JavaScript 前端开发 索引
JavaScript ES6及后续版本:新增的常用特性与亮点解析
JavaScript ES6及后续版本:新增的常用特性与亮点解析
41 4
|
1月前
|
JavaScript 前端开发 开发者
掌握Node.js中的异步编程:从回调到async/await
Node.js的异步编程模型是其核心特性之一,它使得开发者能够构建高性能和高并发的应用程序。本文将带你从Node.js的异步编程基础开始,逐步深入到回调函数、Promises、以及最新的async/await语法。我们将探讨这些异步模式的原理、使用场景和最佳实践,并通过实例代码展示如何在实际项目中应用这些概念。