Promise学习(四)异步编程的终极解决方案async + await:用同步的方式去写异步代码
目录
前言
1. async 函数
ES2017 标准引入了 async
函数,使得异步操作变得更加方便。 async
函数是使用 async
关键字声明的函数。 async
函数是AsyncFunction构造函数的实例, 并且其中允许使用 await
关键字。async
和 await
关键字让我们可以用一种更简洁的方式写出基于Promise的异步行为,而无需刻意地链式调用promise。
async
是一个加在函数前的修饰符,被async
定义的函数会默认返回一个Promise对象resolve
的值。因此对async
函数可以直接then
,返回值就是then方法传入的函数。
- 函数的
返回值
为 promise 对象 - promise 对象的结果由
async
函数执行的返回值决定
async function main() {
//1. 如果返回值是一个非Promise类型的数据
// return 521; // 成功
//2. 如果返回的是一个Promise对象
// return new Promise((resolve, reject) => {
// // resolve('ok'); // 成功
// reject('err'); // 失败
// });
//3. 抛出异常
throw "失败了"; // 失败
};
let result = main();
console.log(result);
可以看出执行的结果由
async
函数的返回值状态来决定
2. await表达式
await
也是一个修饰符,只能放在async
定义的函数内。可以理解为等待
。await
后面可以跟任何的JS 表达式
。虽然说 await
可以等很多类型的东西,但是它最主要的意图是用来等待 Promise 对象的状态被 resolved。如果await
的是 promise对象会造成异步函数停止执行,并且等待 promise 的解决,如果等的是正常的表达式则立即执行
注意:
1.
await
可以理解为是async wait
的简写。await
必须出现在async
函数内部,不能单独使用。async
函数可以单独使用
2.await
主要是对Promise对象成功的结果获取
3. 如果await
的 promise 失败了, 就会抛出异常, 需要通过try...catch
捕获处理
await
右侧的表达式一般为 promise 对象, 但也可以是其它的值- 如果表达式是 promise 对象,
await
返回的是 promise 成功的值 - 如果表达式是其它值, 直接将此值作为
await
的返回值
成功状态:
const main = async () => {
let p = new Promise((resolve, reject) => {
resolve('ok');
});
// 1. 右侧为promise的情况
let res1 = await p;
console.log(res1); // ok
//2. 右侧为其他类型的数据
let res2 = await 20;
console.log(res2); // 20
}
main();
失败状态(try...catch
捕获处理):
const main = async () => {
let p = new Promise((resolve, reject) => {
reject('err了');
});
// 如果promise是失败的状态
try {
let res3 = await p;
} catch(reason) {
console.log(reason);
}
}
main();
3. 解决异步编程的终极方案 async + await
先说一下Promise中各个任务状态的理解:
- Promise==>异步
- await==>异步转同步
- async==>同步转异步
举一个回调地狱的异步例子:(回调地狱是指:多层嵌套函数,函数的返回值是下一个函数的执行条件。)
setTimeout(() => {
console.log('1');
setTimeout(() => {
console.log('2');
setTimeout(() => {
console.log('3');
}, 1000);
}, 2000);
}, 3000);
它会每隔一秒依次输出
'1'
,'2'
,'3'
,回调地狱的缺点是不便于阅读 不便于异常处理
解决方案:
- 原生Promise 链式调用的方式在前面的 Promise学习(一)Promise是什么?怎么用?回调地狱怎么解决?中有提到过,感兴趣的可以去看看哦!
- 终极方案 async + await
const test1 = n => {
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve(n)
}, 1000);
});
}
const test = async () => {
console.log(await test1(1));
console.log(await test1(2));
console.log(await test1(3));
}
test();
这样做的
好处
是:结构清晰
,便于阅读
,方便异常处理
(如果抛出了异常,可以用try...catch
处理异常),这里为了方便理解,直接用的成功的状态。
4. async 和 await 结合实践1:读取文件信息
小案例1:读取 1.html 2.html 3.html 三个文件内容,有两种方法,一是回调函数
形式实现,二是 async
+ await
实现。
1. 回调函数形式:
// 引入fs模块
const fs = require('fs');
//回调函数的方式
fs.readFile('./resource/1.html', (err, data1) => {
if (err) throw error;
fs.readFile('./resource/2.html', (err, data2) => {
if (err) throw error;
fs.readFile('./resource/3.html', (err, data3) => {
if (err) throw error;
console.log(data1 + data2 + data3);
});
});
});
打开集成终端输入 node .\当前要运行的文件名
,即可以读取三个文件内容。
2.
async
+await
实现:
// 引入fs模块
const fs = require('fs');
//引入 util 模块
// util.promisify 方法可以将函数直接变成promise的封装方式,不用再去手动封装
const util = require('util');
const mineReadFile = util.promisify(fs.readFile);
//async 与 await 实现
const main = async () => {
try{
//读取第一个文件的内容
let data1 = await mineReadFile('./resource/1xxx.html'); // 错误文件
let data2 = await mineReadFile('./resource/2.html');
let data3 = await mineReadFile('./resource/3.html');
console.log(data1 + data2 + data3);
}catch(e){
console.log(e); // 错误会提示
console.log(e.code); // 错误中的一个属性
}
}
main();
如果文件名都是正确的会得到和回调函数形式实现的一样的结果,当我们输入错误的文件名时,最后可以得到
try...catch
处理的错误的相关信息
5. async 和 await 结合实践2:结合Ajax获取接口信息
小案例2:结合Ajax获取接口信息, async
+ await
实现。
<!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>
<script src="https://cdn.bootcdn.net/ajax/libs/jquery/3.6.0/jquery.min.js"></script>
</head>
<body>
<button id="btn">点击获取段子</button>
<script>
const sendAJAX = url => {
return new Promise((resolve, reject) => {
const xhr = new XMLHttpRequest();
xhr.responseType = 'json';
xhr.open("GET", url);
xhr.send();
//处理结果
xhr.onreadystatechange = function(){
if(xhr.readyState === 4){
//判断成功
if(xhr.status >= 200 && xhr.status < 300){
//成功的结果
resolve(xhr.response);
}else{
reject(xhr.status);
}
}
}
})
}
$("#btn").click(async () => {
// 获取接口文字信息
let datas = await sendAJAX('https://pagead2.googlesyndication.com/getconfig/sodar?sv=200&tid=gda&tv=r20220718&st=env');
console.log(datas);
});
</script>
</body>
</html>
注意接口的跨域问题
这里提一下
跨域问题
,因为不会自己写接口,网上找的接口很多都有跨域问题,所以我们在选择接口是要看清网络
中有无CORS错误
的字眼,跨域的接口是不能成功返回数据的。
至此,Promise学习篇暂时以完结,如果还有补充后续会继续更新,如果觉得对你有帮助的话请继续支持哦!
Authors: min
时间: 2022年7月27日