• 第二十一节: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 : 原创博客请在转载时保留原文链接或在文章开头加上本人博客地址,否则保留追究法律责任的权利。
     
  • 相关阅读:
    转:MongoDB · 引擎特性 · journal 与 oplog,究竟谁先写入?
    Oracle 11g R2(11.2.0.4) RAC 数据文件路径错误解决--ORA-01157 ORA-01110: 数据文件
    Oracle 高可用作业测试
    Oracle 常见进程
    Mycat-server-1.6.5 常见分片方式
    转:三思!大规模MySQL运维陷阱之基于MyCat的伪分布式架构
    RabbitMQ 安装
    cmd 切换目录和配置环境变量和Curl批量执行Url
    sql中表变量
    Core 项目下使用SQl语句
  • 原文地址:https://www.cnblogs.com/yaopengfei/p/16118744.html
Copyright © 2020-2023  润新知