文章出自个人博客https://knightyun.github.io/2019/08/02/js-async-await,转载请申明


async

概念

用于声明异步函数,返回值为一个 Promise 对象,它以类似 同步 的方式来写异步方法,语法与声明函数类似,例如:

async function fn() {console.log('Hello world!');
}console.log(fn().constructor); // Promise()
// 这里证明其返回值为一个 Promise 对象;

返回值

也许这里会有疑问,返回值是 Promise 对象,那么函数本身定义的返回值跑到哪里去了呢?其实,熟悉 Promise 的就知道其异步结果是通过 .then() 或者 .catch() 方法来获取并进行进一步处理的,这样一个道理,定义的异步函数中的返回值会当成 resolve 状态来处理,一般用 .then() 方法处理,而如果定义的异步函数抛出错误,例如变量未定义,则会被当做 reject 状态来处理,一般使用 .catch() 方法来处理;

举例:

// 使用 .then() 的情况
async function fn1() {return 'Hello world!';
}fn1().then(function(res) {console.log(res);
});
// Hello world!// 使用 .catch() 的情况
async function fn2() {console.log(aaa); // 这里的变量 aaa 未定义,为了制造错误
}fn2().catch(function(error) {console.log(error);
});
// ReferenceError: aaa is not defined

假如是既有返回值,又有错误的话,来看看结果如何:

async function fn3(){console.log(aaa); // aaa 依然未定义;return 'Hello world!';
}fn3().then(function(res){console.log(res);
}).catch(function(error){console.log(error);
});
// ReferenceError: aaa is not defined

结果证明只会执行 reject 状态的情况下的语句,忽略了 resolve 时的代码,所以此处值得 注意

await

概念

用法顾名思义,有 等待 的意思,语法为:

var value = await myPromise();

所谓 等待 其实就是指暂停当前 async function 内部语句的执行,等待后面的 myPromise() 处理完返回结果后,继续执行 async function 函数内部的剩余语句;myPromise() 是一个 Promise对象,而自定义的变量 value 则用于获取 Promise 对象返回的 resolve 状态值;

用法

值得 注意 的是,await 必须在 async function 内使用,否则会提示语法错误;如果 await 后面跟的是其他值,则直接返回该值:

async function fn() {console.log(1);var result = await new Promise(function(resolve, reject) {setTimeout(function(){resolve(2);}, 2000);});console.log(result);console.log(3);console.log(await 4); // 4 会被直接返回
}
fn();
// 1
// 2 (2 秒后输出)
// 3
// 4

如果不用获取返回值,也可以直接执行语句:

async function fn() {console.log(1);await new Promise(function(resolve, reject) {setTimeout(function() {console.log(2);resolve(0);}, 2000);});console.log(3);
}
fn();
// 1
// 2 (2 秒后)
// 3

返回结果

如之前所说,await 会等到后面的 Promise 返回结果 后才会执行 async 函数后面剩下的语句,也就是说如果 Promise 不返回结果(如 resolve 或 reject),后面的代码就不会执行,例如:

async function fn() {console.log(1);await new Promise(function(resolve, reject) {setTimeout(function() {console.log(2);}, 2000);});console.log(3);
}
fn();
// 1
// 2 (2 秒后输出,并且后面不会继续输出 3)

这里可以理解为函数会一直等待 await 返回结果(resolve / reject)才会执行剩下的语句,没有返回结果就会一直等下去,也就一直等不到剩下的语句执行了(还挺痴情-_-);

如果 await 后面的 Promise 返回一个 reject 状态的结果的话,则会被当成错误在后台抛出,例如:

async function fn() {console.log(1);var result = await new Promise(function(resolve, reject) {setTimeout(function() {reject(2);}, 2000);});console.log(3);
}
fn();
// 1
// Uncaught (in promise) 2 (2 秒后输出)

如上,2 秒后会抛出出错误,并且 3 这个数并没有被输出,说明后面的执行也被忽略了;

匿名函数

async 也可以用于申明匿名函数用于不同场景,或者嵌套使用 async 函数,如 await async 的形式,只是要在 await 后面使用 async 形式的函数的话,需要这个函数立即执行且有返回值;

let fn = async function() {let a = await (async function() {console.log(1);return 2;})();console.log(a);async function fn2() {return 3;}console.log(await fn2());
}
fn();
// 1
// 2
// 3

另外,await 后面的 Promise 返回的 reject, 也可以被该 async 函数返回的 Promise 对象以 reject 状态获取,例如:

async function fn() {console.log(1);var result = await new Promise(function(resolve, reject) {setTimeout(function() {reject(2);}, 2000);});console.log(3);
}
fn().catch(function(error) {console.log(error);
});
// 1
// 2 (2 秒后输出)

这种情况就不会以错误抛出,直接对异常值进行了处理,并且最后同样没有输出数字 3,即后面的代码依然被忽略了;

注意事项

非 await 部分

async/await 函数以同步的方式书写异步函数确实方便了不少场景,如定义所讲,函数内部遇到 await 会等到返回结果再继续执行下去,也就是说,非 await 部分仍然会以正常的异步或同步方式执行,例如遇到 setTimeout() 就会放入任务队列等待同步语句执行完后再执行;

比如以下情况:

async function fn() {console.log(0);await new Promise(resolve => {setTimeout(() => {console.log(1);resolve();}, 1000);});setTimeout(() => {console.log(2);}, 0);console.log(3);
}fn();
// 0
// 1(2 秒后)
// 3
// 2

await 内部

虽然说函数会等待 await 返回结果在继续执行,但是 await 内部的代码也依然按正常的同步和异步执行,例如:

async function fn() {console.log(0);setTimeout(() => {console.log(1);}, 0);await new Promise(resolve => {setTimeout(() => {console.log(2);}, 0);console.log(3);setTimeout(() => {console.log(4);resolve();}, 1000);setTimeout(() => {console.log(5);}, 0);});setTimeout(() => {console.log(6);}, 0);console.log(7);
}fn();
// 0
// 3
// 1
// 2
// 5
// 4(2 秒后)
// 7
// 6

上面的代码中返回结果的函数 resolve() 是在 setTimeout() 这个 异步任务 中,所以其被丢到事件队列中等待 2 秒再执行,由于此时 await 还未返回结果,所以还不会去执行 await 以外的代码(输出 7、6),而是先执行同为异步任务、但延时较短的输出 1、2、5 的代码;2 秒后结果返回了,就会继续正常执行 await 以外的同步任务和异步任务了;

但是假如 await 代码内返回结果的函数(resolve() 或 reject())是在 同步任务 中执行的话,情况就有些不一样了,例如:

async function fn() {console.log(0);setTimeout(() => {console.log(1);}, 0);await new Promise(resolve => {setTimeout(() => {console.log(2);}, 0);console.log(3);resolve();console.log(4);setTimeout(() => {console.log(5);}, 0);});setTimeout(() => {console.log(6);}, 0);console.log(7);
}fn();
// 0
// 3
// 4
// 7
// 1
// 2
// 5
// 6

由于同步任务 先于 异步任务执行的机理,在同步任务执行过程中依次输出了 0、3 后,就立即执行了 resolve() 使得 await 得到了返回结果,再往后就继续同步的输出了 4,但是输出 5 的代码是异步任务,与输出 1、2 的代码一并放入任务队列,此时由于 await 返回了结果,所以可以执行 await 以外的代码了,输出 6 是异步任务,于是先输出了同步任务的 7,同步任务都执行完了,最后执行任务队列中的异步任务,按之前进入队列的顺序,就是依次输出 1、2、5、6,所有代码运行结束;

函数嵌套

当 async 函数中嵌套着其他 async 函数时,执行过程可能又有些和预想的不一样,先来看下面的例子:

async function fn() {console.log(0);setTimeout(() => {console.log(1);}, 0);(async function() {console.log(2);setTimeout(() => {console.log(3);}, 0);await new Promise(res => setTimeout(res, 1000))setTimeout(() => {console.log(4);}, 1000);console.log(5);})()console.log(6)
}fn();
// 0
// 2
// 6
// 1
// 3
// 5(1 秒后)
// 4(再等 1 秒后)

也许会疑惑,不是说 async 函数会等到 await 返回结果后再继续执行吗,为何就先输出 6 了?其实不要混淆概念,确实 async 函数内部是这样干的(3 后 1秒输出 5、4),但 async 函数它自身执行时依然是正常的同步任务执行,也就是虽然内部的 async 函数会等待其 await 返回结果才继续执行后面的代码,但外部的 async 函数可不会等待内部的那个 await,会照常执行(你不是我的菜,天涯何处无芳草╮(╯▽╰)╭);

如果确实需要等待这个嵌套的 async 函数执行完再执行剩下的代码,那么前面加个 await 就行了,原理是也是可行的,因为 async 函数就是返回的一个 Promise 函数,代码如下:

async function fn() {console.log(0);setTimeout(() => {console.log(1);}, 0);await (async function() {console.log(2);setTimeout(() => {console.log(3);}, 0);await new Promise(res => setTimeout(res, 1000))setTimeout(() => {console.log(4);}, 1000);console.log(5);})()console.log(6)
}fn();
// 0
// 2
// 1
// 3
// 5(1 秒后)
// 6
// 4(再等 1 秒后)

这里也要 注意,假如嵌套的 async 函数中的 await 不返回结果,并且没有在嵌套的 async 函数前面添加 await,那么外部的 async 函数内部剩余的代码也不会执行;


技术文章推送 手机、电脑实用软件分享

async 与 await 的用法详解相关推荐

  1. C# 中的Async 和 Await 的用法详解

    众所周知C#提供Async和Await关键字来实现异步编程.在本文中,我们将共同探讨并介绍什么是Async 和 Await,以及如何在C#中使用Async 和 Await. 同样本文的内容也大多是翻译 ...

  2. 【转载】 C# 中的Async 和 Await 的用法详解

    众所周知C#提供Async和Await关键字来实现异步编程.在本文中,我们将共同探讨并介绍什么是Async 和 Await,以及如何在C#中使用Async 和 Await. 同样本文的内容也大多是翻译 ...

  3. Python中下划线 _ 的最全用法详解

    Python中下划线 _ 的最全用法详解 '_'是什么? _在python中可以作为一个标识符,用于定义变量和方法唯一名称.同时它也是Python中的一个软关键字,指在某些特定上下文中保留的关键字.截 ...

  4. linux mount命令参数及用法详解

    linux mount命令参数及用法详解 非原创,主要来自 http://www.360doc.com/content/13/0608/14/12600778_291501907.shtml. htt ...

  5. html js require函数,Require.js的基本用法详解

    一:什么是require.js ①:require.js是一个js脚本加载器,它遵循AMD(Asynchronous Module Definition)规范,实现js脚本的异步加载,不阻塞页面的渲染 ...

  6. CyclicBarrier 用法详解

    CyclicBarrier 用法详解 CyclicBarrier使用场景 用于协调多个线程同步执行操作的场合,所有线程等待完成,然后一起做事情( 相互之间都准备好,然后一起做事情 ) 例如百米赛跑,必 ...

  7. Require.js的基本用法详解

    一:什么是require.js ①:require.js是一个js脚本加载器,它遵循AMD(Asynchronous Module Definition)规范,实现js脚本的异步加载,不阻塞页面的渲染 ...

  8. CountDownLatch用法详解

    CountDownLatch用法详解 CountDownLatch使用场景 线程计数器 用于线程执行任务,计数 等待线程结束 用法一: 等待所有的事情都做完 //程序计数器CountDownLatch ...

  9. python argv 详解_Python3 sys.argv[ ]用法详解

    sys.argv[]说白了就是一个从程序外部获取参数的桥梁,这个"外部"很关键,因为我们从外部取得的参数可以是多个,所以获得的是一个列表(list),也就是说sys.argv其实可 ...

最新文章

  1. python 时间戳 字符串_python time模块 字符串 时间戳互转
  2. canvas绘制正文形并填充-2
  3. VFP下利用API调用帮助
  4. 百度春晚红包分享链接遭微信屏蔽 已有超700人集齐好运卡
  5. arraylist插入数据_集合系列 List(二):ArrayList
  6. 13、MVC 设计思想
  7. 10的负2次方用计算机怎么算,2的负二次方等于多少(进制转换计算器)
  8. ubantu下安装搜狗输入法
  9. win10系统Nessus下载插件错误
  10. Win7连接网络打印机步骤
  11. word 尾注 脚注_如何在Microsoft Word中使用脚注和尾注
  12. Mariadb数据库
  13. Some WSL system related access rights are not set correctly. This sometimes happens after waking the
  14. Shiro学习笔记(二):自定义Realm的实现
  15. LeetCode 题解:一顿操作猛如虎,一看击败百分五
  16. 凯悦酒店集团完成收购Apple Leisure Group;复星旅文在第四届进博会上完成近10项国际合作签约 | 全球旅报...
  17. html5网页制作代码-我的班级网页 HTML期末大作业 学校班级网页制作模板
  18. LT8618SX 是龙迅的低功耗版本 HDMI 发射器,其基于 ClearEdgeTM 技术
  19. 易语言大漠内存找图的实现
  20. oracle 创建emp表,dept表

热门文章

  1. Qt学习: QFileDialog和QMessageBox的用法和程序示例
  2. Gallery 相册管理系统项目将不再维护
  3. 深入理解HTTP协议、HTTP协议原理分析【转】
  4. 微信分享调用 -- c#篇
  5. Gradle学习之使用java plugin
  6. Mysql设置允许外网访问(图文)
  7. 那些在11gR2中可能惹祸的新特性,一张列表帮助你摆脱升级11gR2带来的烦恼
  8. 遍历指定目录并且从子目录开始将目录输出到指定文件
  9. Strings_append_学习
  10. 在windows上搭建redis集群(主从复制)