async函数
简介
async函数是使用async关键字声明的函数。
async函数是AsyncFunction构造函数的实例, 并且其中允许使用await关键字。
async和await关键字让我们可以用一种更简洁的方式写出基于Promise的异步行为,而无需刻意地链式调用promise。
await关键字只在async函数内有效。如果在async函数体之外使用它,就会抛出语法错误 SyntaxError 。
async/await的目的为了简化使用基于promise的API时所需的语法。
async/await的行为就好像搭配使用了生成器和promise。
async函数介绍
async函数会返回一个promise对象。
promise对象的结果由async函数执行的返回值决定。
如果一个async函数的返回值看起来不是promise,那么它将会被隐式地包装在一个promise中。
如果async函数的返回值是promise对象,那么async函数的返回值就是这个promise对象的成功或失败的值决定。
如果async函数抛出错误,那么返回结果就是一个失败的promise对象
如下代码:
//async函数返回一个 Promise 对象。
async function foo() {
return 1;
// 出错就抛出错误,返回结果是一个失败的promise对象
// throw new Error('出错了!');
}
等价于:
function foo() {
return Promise.resolve(1)
}
await表达式
1、await 必须写在async函数中
2、await右侧的表达式一般为promise对象
3、await返回的是promise成功的值
4、await的promise失败就会抛出异常,需通过try。。。catch捕获。
如果await返回的是promise成功的值,
<body>
<script>
// 创建一个promise对象
const p = new Promise((resolve, reject) => {
resolve("用户数据");
})
// async函数结合await使用
async function main(){
// await关键字后面接promise对象,并且await返回的是promise成功的值
let result = await p;
console.log(result);
}
// 调用函数
main(); // 结果:用户数据
</script>
</body>
如果await返回的是promise失败的值,则需要使用try…catch捕获
<body>
<script>
// 创建一个promise对象
const p = new Promise((resolve, reject) => {
// resolve("用户数据");
reject("失败了!")
})
// async函数结合await使用
async function main(){
// 使用try catch捕获失败
try{
// 如果promise对象失败了,那么try会进行一个捕获,然后放到catch中进行处理
let result = await p;
console.log(result);
}catch(e){
console.log(e);
}
}
// 调用函数
main();
</script>
</body>
异步函数示例:
async function foo() {
const result1 = await new Promise((resolve) => setTimeout(() => resolve('1')))
const result2 = await new Promise((resolve) => setTimeout(() => resolve('2')))
}
foo();
async与await结合使用
读取文件
假设同级目录FILE下有三个文件first.txt,second.txt,third.txt。
// CJS模块化导入fs
const fs = require("fs");
// 读取第一个文件
function readFirst(){
return new Promise((resolve,reject) =>{
fs.readFile("../FILE/first.txt",(err,data) =>{
// 失败则返回失败结果
if(err) reject(err);
// 成功则返回成功数据
resolve(data);
})
})
}
// 读取第二个文件
function readSecond(){
return new Promise((resolve,reject) =>{
fs.readFile("../FILE/second.txt",(err,data) =>{
// 失败则返回失败结果
if(err) reject(err);
// 成功则返回成功数据
resolve(data);
})
})
}
// 读取第三个文件
function readThird(){
return new Promise((resolve,reject) =>{
fs.readFile("../FILE/third.txt",(err,data) =>{
// 失败则返回失败结果
if(err) reject(err);
// 成功则返回成功数据
resolve(data);
})
})
}
// 使用async函数实现异步编程
async function main(){
// 获取first.txt文件内容
let first = await readFirst();
// 获取second.txt文件内容
let second = await readSecond();
// 获取third.txt文件内容
let third = await readThird();
console.log(first.toString());
console.log(second.toString());
console.log(third.toString());
}
// 调用async函数
main();
结果:
这是第一个文件
这是第二个文件
这是第三个文件
表面上和同步调用很像,其实是在前一步完成的基础上再去进行下一步操作。
发送ajax请求
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<script>
// 发送ajax请求,返回值为promise对象
function sendAJAX(url){
return new Promise((resolve,reject) => {
// 创建ajax对象
const x = new XMLHttpRequest();
// 初始化
x.open('get',url);
// 发送
x.send();
x.onreadystatechange = function(){
if(x.readyState == 4 && x.status >= 200 && x.status < 300){
// 成功
resolve(x.response);
}else{
reject(x.status);
}
}
})
}
// 使用async和await测试
async function main(){
let result = await sendAJAX("某个get请求的url");
console.log(result);
}
// 调用
main();
</script>
</body>
</html>
- 异步操作为什么要进行同步化?
希望程序异步执行,就是为了 “跳过” 阻塞,减少时间花销,或者避免回调地狱。关于异步方案, ES6
使用了 基于状态管理的 Promise
。与之相反的是,如果需要一系列的异步 “串行”,则需要保证信息/消息的顺序性时,就需要异步操作进行同步化,例如多个ajax的请求需要按照队列返回再依次处理,保证客户端的请求与响应顺序。
如果说 Promise
主要解决的是异步回调问题,那么 async + await
主要解决的就是将异步问题同步化,降低异步编程的认知负担。
- 如果不存在继发关系的异步操作,如何让他们同时触发
有如下异步操作
let foo = await getFoo();
let bar = await getBar();
上面代码中,getFoo
和getBar
是两个独立的异步操作(即互不依赖),被写成继发关系。这样比较耗时,因为只有getFoo
完成以后,才会执行getBar
,完全可以让它们同时触发。
// 写法一
let result = await Promise.all([getFoo(), getBar()]);
// 写法二
let fooPromise = getFoo();
let barPromise = getBar();
let foo = await fooPromise;
let bar = await barPromise;
上面两种写法,getFoo
和getBar
都是同时触发,这样就会缩短程序的执行时间。