大部分同学了解Promise,也知道async await可以实现同步化写法,但实际上对一些细节没有理解到位,就容易导致实际项目中遇到问题。
开始先抛结论,下文将针对主要问题点进行论述。
1、所有async方法调用,必须加await或catch,捕获错误;如果最上层的async方法是被框架(react、egret)调用的,无法加await,则需要在这个async方法内做好try catch,不要把报错抛到框架层;
2、async方法,实际返回了一个promise,默认把return值作为promise的resolve内容,而报错则封装为promise的reject;
3、async方法内那么遇到异常要终止,可以直接throw ‘xxx’/Error;
4、async方法内如果有调用下一层方法(这个方法是async方法或返回Promise),则需要加await,等待这个promise结果;如果同时要返回该下层调用的return值,则可以省略await,改为直接return这个Promise(但不建议,还是统一await同步写法比较好理解,详见下文例子);
5、async方法如果正常执行,则直接执行完,return即可,不需要自行创建一层promise。 
 

1. 为什么async方法一定要加await或catch?

这里,需要先看一个例子,大家看看有什么问题。

main();

async function main() {
try {
loadImage();
loadConfig();
} catch (e) {
console.log('main', e);
}
} function loadImage(){
return new Promise((resolve, reject) => {
setTimeout(reject, 1000, 'network error');
});
} async function loadConfig(){
throw 'logic bug';
await wait();
console.log('config ok');
} function wait(){
return new Promise((resolve, reject) => {
setTimeout(resolve, 1000);
});
}

答案公布:

无法捕获loadImage和loadConfig的报错。

上述代码是一个典型,实际是从项目某个同学代码中抽象得来的。虽然看起来很工整很稳健,try catch做的很到位,但实际上,他没有把async和await理解透彻,没有理解到async返回的是Promise,无论是async内同步的报错还是异步(延迟)的报错,对上层调用来说,都是一个微任务。

要解决上述问题,关键点就是,调用loadImage和loadConfig时,加await。

async function main() {
try {
await loadImage();
await loadConfig();
} catch (e) {
console.log('main', e);
}
}

所以,调用async方法,不加await,就类似一个耍流氓行为,等同于使用Promise但不加catch。

另外,最顶层的方法main再被调用时,由于没有包裹在async内,无法使用await,此时我们可以在main()后加上catch(),因为async方法实际返回的是Promise。题外话:目前top-level await还没有正式成为标准,但最新V8引擎里边已经可以使用(https://v8.dev/features/top-level-awaithttps://github.com/tc39/proposal-top-level-await

2. 为什么async方法内不要return Promise?

先看一个典型的例子

async function main() {
try {
const result = await load(url);
//...
} catch (e) {
console.error(e);
}
} async function load(url) {
if (!url) {
return Promise.reject('url is invalid');
} else {
const result = await fetch(url); //代表一个异步操作
return Promise.resolve(result);
}
}

大家再看看这段代码是否有问题?

答案公布:

运行时,实际没有问题,逻辑是正常的,也能捕获错误。但是,有一些不足,多了一层Promise,会导致性能下降(新版本chrome解决了),而且影响回调执行时机。

接下来通过两个代码对比一下,大家会更清楚。

代码片段1

console.log('script start');

async function async1() {
await async2();
console.log('async1 end');
} async function async2() {
console.log('async2 end');
} async1();
setTimeout(function() {
console.log('setTimeout');
}, 0);
new Promise(resolve => {
console.log('Promise');
resolve();
}).then(function() {
console.log('promise end');
});
console.log('script end');

代码片段2

console.log('script start');

async function async1() {
await async2();
console.log('async1 end');
} async function async2() {
console.log('async2 end');
return Promise.resolve().then(()=>{ console.log('async2 end in promise') })
} async1();
setTimeout(function() {
console.log('setTimeout');
}, 0);
new Promise(resolve => {
console.log('Promise');
resolve();
}).then(function() {
console.log('promise end');
});
console.log('script end');

对比一下chrome控制台运行结果:

左(片段1)   右(片段2)

    

不同点就是,async1中await async2的时间推迟了,排在另外一个promise微任务之后。

通过这例子可见,虽然async方法里边return一个Promise和直接return 值 并没有明显的差异,但会在调用时机上产生一些微妙的变化。

所以,总体来说,不建议在async方法中再return或reject一个Promise。

3. 参考写法

最后,综合上述结论,提供一些参考写法,大家可以按需取用。

main().catch(()=>{});   // 顶层调用,如果没有async包裹就用catch,如果是框架内调用,则在main函数体中做好catch

async function main() {
try {
const result = await load(url);
//...
} catch (e) {
// 所有try内的async方法均有await,所有错误都会层层抛出,直到这里捕获
console.error(e);
}
} async function load(url) {
if (!url) {
throw 'url is invalid'; // 直接throw错误信息,简洁明了,直接中断后续流程
} const config = await fetch(url); // 假如fetch接口是一个网络获取,接收url,返回一个Promise
return await runTask(config); //代表一个异步操作
// return runTask(config); // 和上一行,两种做法都可以,这里是return语句,可以把promise当做async方法的return值,上层await会解开。但为了方便记忆,不建议使用这个方式,应该统一使用await。
} async function runTask(data) {
// 对接一个不支持Promise的第三方库,我们只需要在最下层方法,包一个promise
return new Promise((resolve, reject) => {
thirdPartyRun(data, (res) => {
resolve(res); // 这里返回数据
}, (e) => {
reject(e); // 这里可以做一些错误信息转换
});
});
} // 代表一个不支持Promise的第三方库,如何对接到async await体系
function thirdPartyRun(data, success, fail) {
//...
}

async await 你真的用对了吗?的更多相关文章

  1. 实际案例:在现有代码中通过async/await实现并行

    一项新技术或者一个新特性,只有你用它解决实际问题后,才能真正体会到它的魅力,真正理解它.也期待大家能够多分享解一些解决实际问题的内容. 在我们遭遇“黑色30秒”问题的过程中,切身体会到了异步的巨大作用 ...

  2. 在现有代码中通过async/await实现并行

    在现有代码中通过async/await实现并行 一项新技术或者一个新特性,只有你用它解决实际问题后,才能真正体会到它的魅力,真正理解它.也期待大家能够多分享解一些解决实际问题的内容. 在我们遭遇“黑色 ...

  3. node.js异步控制流程 回调,事件,promise和async/await

    写这个问题是因为最近看到一些初学者用回调用的不亦乐乎,最后代码左调来又调去很不直观. 首先上结论:推荐使用async/await或者co/yield,其次是promise,再次是事件,回调不要使用. ...

  4. (译文)学习ES6非常棒的特性——Async / Await函数

    try/catch 在使用Async/Await前,我们可能这样写: const main = (paramsA, paramsB, paramsC, done) => { funcA(para ...

  5. 重构:从Promise到Async/Await

    摘要: 夸张点说,技术的发展与历史一样,顺之者昌,逆之者亡.JS开发者们,赶紧拥抱Async/Await吧! GitHub仓库: Fundebug/promise-asyncawait 早在半年多之前 ...

  6. 彻底搞懂 C# 的 async/await

    前言 Talk is cheap, Show you the code first! private void button1_Click(object sender, EventArgs e) { ...

  7. JavaScript基础——深入学习async/await

    本文由云+社区发表 本篇文章,小编将和大家一起学习异步编程的未来--async/await,它会打破你对上篇文章Promise的认知,竟然异步代码还能这么写! 但是别太得意,你需要深入理解Promis ...

  8. JavaScript是如何工作的:事件循环和异步编程的崛起 + 5种使用 async/await 更好地编码方式!

    摘要: 深度理解JS事件循环!!! 原文:JavaScript是如何工作的:事件循环和异步编程的崛起+ 5种使用 async/await 更好地编码方式! 作者:前端小智 Fundebug经授权转载, ...

  9. 一个真实的Async/Await示例

    译者按: 通过真实的代码示例感受Async/Await的力量. 原文: Async/await - A thorough example 译者: Fundebug 为了保证可读性,本文采用意译而非直译 ...

随机推荐

  1. ffmpeg+Python实现B站MP4格式音频与视频的合并

    目录 安装 官网下载 环境变量 验证 ffmpeg的使用 Python实现自动处理 文件结构 番剧缓存结构 常规缓存结构 文件信息 代码 具体代码 代码说明 安装 官网下载 http://ffmpeg ...

  2. git学习(十一) idea git pull 解决冲突

    测试如下: 先将远程的代码修改,之后更新: 之后将工作区修改的代码(这里修改的代码跟远程修改的位置一样)提交到本地,之后拉取远程的代码,会发现有冲突: Accept Yours 就是直接选取本地的代码 ...

  3. 前端基础——HTML(二)

    HTML标签 标题标签<h1>.<h2>.<h3>.<h4>,标题标签之间是不能嵌套的,下一级标题与上级标题之间通过同级 关于书写,下级标题解释说明的是 ...

  4. tomcat 启动失败

    1.tomcat单独通过脚本可以启动,但是http://localhost:8080加载页面失败 tomcat 启动失败: a.检查JAVA_HOME和CATALINA_HOME是否配置正确:或者直接 ...

  5. Maven2 ---- 安装及配置

    Maven详解(二)------ Maven的安装配置   目录 1.下载 Maven 2.配置 Maven 环境变量 3.查看 Maven 环境变量是否配置成功 4.在 eclipse 中集成 Ma ...

  6. 在VirtualBox中调整Raspbian分辨率

    参考自一路阳光随行发表的virtualBox设置虚拟机分辨率大小中ubuntu虚拟机分辨率的设置方法. 启动Raspbian虚拟机,点击 窗口主菜单里的设备->安装增强功能.系统后会自动挂载增强 ...

  7. Reading Face, Read Health论文阅读笔记

    摘要: 随着计算技术觉.人工智能和移动技术的发展,利用计算机读脸技术去识别每个人每天的健康是可行的.怎么去设计一个基于FRT(face reading technologies)的用于得到每天的保健实 ...

  8. 4G工业路由器的性能介绍和应用需求

    4G工业路由器可以实现数据的远程传输和设备控制功能,主要应用的场景包括智能电网.智能交通.智能家居.才智金融.工业自动化.公共安全.环境保护.数字化医疗等领域,特别是大数据或是视频传输等.那么4G工业 ...

  9. MySQL连接报错(错误:1130)

    1. 原因 显示这个错误是因为没有连接到该机器MySQL数据库的权限,这是在一开始安装MySQL是默认设置的. 在数据库mysql下有一张名为`user`的表,其中的字段user下的root的值默认为 ...

  10. 解决SBT下载慢,dump project structure from sbt?

    一. 安装SBT,参考https://blog.csdn.net/zcf1002797280/article/details/49677881 二. 在~/.sbt下新建repositories添加如 ...