为什么async/await关键字是如此重要

现在JS里有async/await了,处理异步代码几乎不再有什么争议,但还是会有人有疑问,为什么不把所有函数都定义成async的,然后所有函数调用都写成await的,这样最终不就可以省略掉所有的async/await关键字了吗(默认隐式async/await)?这样不就达成了“天下无异步”的太平盛世了吗?

只要稍微动点脑筋就不会有这种想法。

我们都知道目前的环境下JS它还是一门单线程的语言,然后通过Event Loop来实现异步IO。虽然也有fibjs这种“异类”,会稍微打破一些认知。基于这个前提,我们就有一些共识,比如:

同一个event里的代码是顺序执行的不可分割的单元,在这里就不需要考虑资源竞争的问题了。
通过callback或者promise方式调用的东西会受到Event Loop的调度,不管它是Macro Task还是Micro Task,反正会进入另一个单元里执行。
那么有如下代码

JavaScript

function A() {
foo()
bar()
}

async function B() {
await foo()
await bar()
}

function A() {
foo()
bar()
}

async function B() {
await foo()
await bar()
}
如果单看这两个函数,如果A、B、foo、bar都没有副作用,那么会觉得这两个函数的效果没什么差别,在这种情况下“默认async/await”似乎是可行的。

但如果有共享资源和竞争,事情就会变得完全不一样。

JavaScript

var shared = 0

function A() {
foo()
bar()
return shared++
}

async function B() {
await foo()
await bar()
return shared++
}
var shared = 0

function A() {
foo()
bar()
return shared++
}

async function B() {
await foo()
await bar()
return shared++
}
如果A和B用到了共享资源,对于A而言,因为是完全同步执行的,那么整个A的代码会在一个event里执行,它是“线程安全”的(这里加引号的是因为这个不是严格的线程安全的概念,只是表示个意思),只要通过静态代码分析的手段就可以得知foo和bar对shared有没有副作用,那么这个A函数的执行结果就是可预知的。

但B则完全不一样,因为await关键字会出让执行权,也就是foo、bar、return不在一个event里执行,那么在这三行代码的“行缝儿”之间就有无数的可能性,这些缝隙里塞进去一万个event也不得而知。这种情况下对foo和bar执行静态分析(去判断他们对shared有没有副作用)是没什么卵用的,因为shared被修改的可能性有无数种,比如触发了一个事件导致别的listener修改了shared。也就是说B函数的执行结果是不能通过静态分析而预知的,它不再是纯函数了(废话)。

这就是async/await的重要性了,它绝对不是一个简单的语言设计的品味问题,不全局省略async/await是因为它明确的告诉写代码的人这个地方会发生什么事情。开发者只要看到它,马上就会对这里的共享资源多提一个心眼,会以完全不一样的眼光去看待B函数。

而对于严肃地写代码、写严肃的代码而言,“知道一行代码会发生什么”这件事有多重要我想不需要再多强调了。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值