第二十一节:async异步函数和await关键字详解、异常处理方案
一. async异步函数
1. 什么是异步函数?
(1).async关键字用于声明一个异步函数.
async是asynchronous单词的缩写,异步、非同步;
sync是synchronous单词的缩写,同步、同时;
(2).async异步函数有很多种写法
{
console.log("---------- 1. 异步函数的几种写法------------");
async function foo1() {}
const foo2 = async function () {};
const foo3 = async () => {};
class Person {
async foo() {}
}
}
2. 异步函数的执行流程
异步函数的内部代码执行过程和普通的函数是一致的(在不使用await关键字的情况下),默认情况下也是会被同步执行。
代码分享:
{
console.log("----------2. 异步函数的执行流程------------");
async function foo() {
console.log("foo function start~");
console.log("内部的代码执行1");
console.log("内部的代码执行2");
console.log("内部的代码执行3");
console.log("foo function end~");
}
console.log("script start");
foo();
console.log("script end");
// 运行结果如下:和普通函数一样
/*
script start
foo function start~
内部的代码执行1
内部的代码执行2
内部的代码执行3
foo function end~
script end
*/
}
3. 异步函数的返回值
异步函数的返回值和普通函数是有区别的, 异步函数的返回值一定是一个Promise,但分下面几种情况:
(1). 异步函数的返回值如果是普通值或对象,该异步函数的返回值回被包裹在Promise.resolve中
注:如果不写返回值,则 return undefined;
{
console.log("--------3.1 返回一个普通的值或者对象----------");
async function foo() {
console.log("foo function start~");
console.log("内部代码执行");
console.log("foo function end~");
return "okok";
}
const promiseResult = foo();
promiseResult.then(res => console.log("result:" + res)); //result:okok
}
不写返回值代码:
{
console.log("--------3.1 不写返回值----------");
async function foo() {
console.log("foo function start~");
console.log("内部代码执行");
console.log("foo function end~");
}
const promiseResult = foo();
promiseResult.then(res => console.log("result:" + res)); //result:undefined
}
(2). 异步函数的返回值是Promise,则状态由Promise内部的是resolve 还是 reject 来决定;
{
console.log("-------- 3.2 返回一个Promise----------");
async function foo() {
console.log("foo function start~");
console.log("内部代码执行");
console.log("foo function end~");
return new Promise((resolve, reject) => {
reject("error了");
});
}
const promiseResult = foo();
promiseResult.catch(err => console.log("result:" + err)); //result:error了
}
(3). 异步函数的返回值是一个对象并且实现了thenable,那么会由对象的then方法来决定
// 3.3 返回一个对象,含then方法
{
console.log("--------3.3 返回一个对象,含then方法----------");
async function foo() {
console.log("foo function start~");
console.log("内部代码执行");
console.log("foo function end~");
return {
then(resolve, reject) {
reject("error了");
},
};
}
const promiseResult = foo();
promiseResult.catch(err => console.log("result:" + err)); //result:error了
}
4. 异步函数中抛出异常
如果我们在async中抛出了异常,那么程序它并不会像普通函数一样报错,而是会作为Promise的reject来传递;
{
console.log("------4. 异步函数中抛出异常--------");
async function foo() {
console.log("foo function start~");
console.log("中间代码~");
// 异步函数中的异常, 会被作为异步函数返回的Promise的reject值的
throw new Error("error message");
console.log("foo function end~");
}
// 异步函数的返回值一定是一个Promise
foo().catch(err => console.log("result", err));
console.log("后续业务~~~~~");
// 运行结果:【foo function end~】没有输出,但是【后续业务~~~~~】输出了
/*
foo function start~
中间代码~
后续业务~~~~~
result Error: error message */
}
二. await关键字详解
1. 使用环境
使用await关键字,必须在async函数中使用,普通函数中没法使用await关键字
{
console.log("------1. await跟一个promise表达式----------");
//封装异步请求函数
function requestData() {
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve({ status: "ok", msg: "获取成功" });
}, 2000);
});
}
// 封装调用函数
async function foo() {
console.log("foo start");
const result = await requestData();
console.log(result);
console.log("中间业务1");
console.log("foo end");
}
// 最终调用
foo();
// 运行结果
/*
foo start
{ status: 'ok', msg: '获取成功' }
中间业务1
foo end
*/
}
2. await用法
(1). await是后面会跟上一个表达式
这个表达式会返回一个Promise, await会等到Promise的状态变成fulfilled状态(resolved),之后继续执行后面的代码;
如下案例:获取到result结果后,才会继续执行后面的代码.
{
console.log("------1. await跟一个promise表达式----------");
//封装异步请求函数
function requestData() {
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve({ status: "ok", msg: "获取成功" });
}, 2000);
});
}
// 封装调用函数
async function foo() {
console.log("foo start");
const result = await requestData();
console.log(result);
console.log("中间业务1");
console.log("foo end");
}
// 最终调用
foo();
// 运行结果
/*
foo start
{ status: 'ok', msg: '获取成功' }
中间业务1
foo end
*/
}
(2). await后面是一个普通的值,那么会直接返回这个值
{
console.log("------2. await后面跟一个普通值----------");
// 封装调用函数
async function foo() {
console.log("foo start");
const result = await "msg";
console.log(result);
console.log("中间业务1");
console.log("foo end");
}
// 最终调用
foo();
// 运行结果
/*
foo start
msg
中间业务1
foo end
*/
}
(3). await后面是一个thenable的对象(即一个对象,实现了then方法),那么会根据对象的then方法调用来决定后续的值;
{
console.log("-----4. await后面跟一个实现含then方法的对象--------");
// 封装调用函数
async function foo() {
console.log("foo start");
const result = await {
then(resolve, reject) {
resolve("ok");
},
};
console.log(result);
console.log("中间业务1");
console.log("foo end");
}
// 最终调用
foo();
// 运行结果
/*
foo start
ok
中间业务1
foo end
*/
}
3. await后面的表达式,返回的Promise是reject的状态 【重点】
会将这个reject结果直接作为await所在函数的Promise的reject值
方案1:需要在await函数的then的第二个回调 或者 catch 中调用。
注意:这种情况下,await后面的代码不在执行
方案2:在foo函数中用try-catch包裹所在的业务来获取
注意:这种情况下,await后面的代码不在执行
方案3:直接在await 后面的调用上 跟catch,但是虽然可以捕获异常,但是await后面的代码依旧会执行 【根据实际场景来决定采用这种写法】
方案1代码:
{
console.log("---5. await后面的表达式,返回的Promise是reject的状态------");
//封装异步请求函数
function requestData() {
return new Promise((resolve, reject) => {
setTimeout(() => {
reject({ status: "error", msg: "获取失败" });
}, 2000);
});
}
// 封装调用函数
async function foo() {
console.log("foo start");
const result = await requestData();
console.log(result);
console.log("中间业务1");
console.log("foo end");
}
// 最终调用
foo().catch(error => console.log(error));
// 运行结果
/*
foo start
{ status: 'error', msg: '获取失败' }
*/
}
方案2代码:
{
console.log("---方式二 try-catch包裹------");
//封装异步请求函数
function requestData() {
return new Promise((resolve, reject) => {
setTimeout(() => {
reject({ status: "error", msg: "获取失败" });
}, 2000);
});
}
// 封装调用函数
async function foo() {
try {
console.log("foo start");
const result = await requestData();
console.log(result);
console.log("中间业务1");
console.log("foo end");
} catch (error) {
console.log(error);
}
}
// 最终调用
foo();
// 运行结果
/*
foo start
{ status: 'error', msg: '获取失败' }
*/
}
方案3代码:
// 写法3:直接在await 后面的调用上 跟catch
{
console.log("---写法3:直接在await 后面的调用上 跟catch----");
//封装异步请求函数
function requestData() {
return new Promise((resolve, reject) => {
setTimeout(() => {
reject({ status: "error", msg: "获取失败" });
}, 2000);
});
}
// 封装调用函数
async function foo() {
console.log("foo start");
const result = await requestData().catch(err => console.log(err));
console.log(result);
console.log("中间业务1");
console.log("foo end");
}
// 最终调用
foo();
// 运行结果
/*
foo start
{ status: 'error', msg: '获取失败' }
undefined
中间业务1
foo end
*/
}
三. 异步处理方案
1. throw关键
(1). 作用
用于抛出一个用户自定义的异常;当遇到throw语句时,当前的函数执行会被停止(throw后面的语句不会执行);
(2). 用法
A. 基本数据类型:比如number、string、Boolean
B. 对象类型:对象类型可以包含更多的信息
C. 返回Error类或其子类 (详见下面)
{
console.log("---------1. throw用法----------");
throw "对不起,抛异常了";
console.log("内部业务1,不能执行了");
console.log("内部业务2,不能执行了");
}
{
console.log("---------1. throw用法----------");
throw { status: "error", msg: "出错了" };
console.log("内部业务1,不能执行了");
console.log("内部业务2,不能执行了");
}
2. Error类型
JavaScript已经给我们提供了一个Error类,我们可以直接创建这个类的对象
(1).Error包含三个属性:
A. messsage:创建Error对象时传入的message;
B. name:Error的名称,通常和类的名称一致;
C. stack:整个Error的错误信息,包括函数的调用栈,当我们直接打印Error对象时,打印的就是stack;
(2).Error有一些自己的子类:
A. RangeError:下标值越界时使用的错误类型;
B. SyntaxError:解析语法错误时使用的错误类型;
C. TypeError:出现类型错误时,使用的错误类型;
{
console.log("---------2.Error类型----------");
throw new Error("出错了", "perison");
console.log("内部业务1,不能执行了");
console.log("内部业务2,不能执行了");
}
{
console.log("---------2.TypeError类型----------");
throw new TypeError("类型出错了");
console.log("内部业务1,不能执行了");
console.log("内部业务2,不能执行了");
}
3. 异常处理机制
一个函数抛出了异常,调用它的时候程序会被强制终止:
这是因为如果我们在调用一个函数时,这个函数抛出了异常,但是我们并没有对这个异常进行处理,那么这个异常会继续传递到上一个函数调用中;
而如果到了最顶层(全局)的代码中依然没有对这个异常的处理代码,这个时候就会报错并且终止程序的运行;
4. try-catch异常捕获
(1). 使用try-catch可以捕获异常
(2). 在ES10(ES2019)中,catch后面绑定的error可以省略。
(3). 当然,如果有一些必须要执行的代码,我们可以使用finally来执行:finally表示最终一定会被执行的代码结构;
{
console.log("---------3. try-catch捕获异常----------");
try {
throw new Error("出错了", "perison");
} catch (error) {
console.log(error);
} finally {
console.log("内部业务1");
console.log("内部业务2");
}
}
!
- 作 者 : Yaopengfei(姚鹏飞)
- 博客地址 : http://www.cnblogs.com/yaopengfei/
- 声 明1 : 如有错误,欢迎讨论,请勿谩骂^_^。
- 声 明2 : 原创博客请在转载时保留原文链接或在文章开头加上本人博客地址,否则保留追究法律责任的权利。