第二十一节: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 : 原创博客请在转载时保留原文链接或在文章开头加上本人博客地址,否则保留追究法律责任的权利。
 
posted @ 2022-04-08 17:55  Yaopengfei  阅读(1308)  评论(3编辑  收藏  举报