对JS中async&&&await的简单理解

类别:信息技术 作者:ATScore 发布日期:2023年8月20日

file

对async/await的简单理解

之前改nodejs代码,遇到async/await,没有好好搞懂。今天温习一下,查了些资料,网上的资料,尤其是某傻乎真是写的奇复杂无比,简单问题搞的这么复杂。

写点心得,以享来者。

async的动机

JS程序可能会有多个线程,在一个线程上,进入一个函数,走出一个函数。所谓线程串行执行。 但是一些函数,它会因为系统调用(system call)进入等待。这样执行效率就不算太高。 JS使用async标注一个函数,这个函数在执行时,是异步的。即,即使这个函数没执行完,在等待的时候,JS也可以继续执行后面的代码。 你以为是async造成的异步执行?其实并不是。

await的设计

JS是在遇到await时才异步执行的。即遇到await函数JS就执行非阻塞操作然后立即就返回,同时(我估计)另开一线程用于执行await及后面的语句。

JS一些古怪的坚持和设计,需要用奇怪的方式来声明非阻塞和等待

说一个优秀设计的例子。 MPI语言,它让handle关联非阻塞操作,并使用显式的wait来等待具体的异步操作。围绕句柄就可以制造出如回调等方式。虽然语言工作在low level上,但这非常直观。

我不喜欢JS的设计。它在async函数内使用await,看似聪明,但却把函数分为了“普通函数”和“异步函数”。事实上,并没有异步函数,只有异步操作,包含异步操作的函数就变成了异步函数,这看上去多少有点牵强。

//使用timeout来模拟长时系统调用,返回字符串
async function test() {  //这里用async或者不戴,都可以
    setTimeout(()=>{}, 1000);
    return "hello world";
}

//异步调用,获取字符串,并打印
async function say() {
    v = await test();
    console.log(v)
}

//执行异步调用,主线会跳过say()中的log,先执行log("end"),然后待say()中的await结束后,打印了hello
say();
console.log("end")

输出: end hello

走一遍例子的控制流

主线:进入say(), 不管say()的async标记,继续走。遇到await test(),非阻塞启动test(),然后立即从say()返回。执行第二句log("end"),于是stdout上显示end。主线结束了。

次线:进入test(),先睡1s,然后返回promise("hello")。(为什么会有promise,原理请自行查询),从test()返回后,await解包promise,将"hello"传给v,并打印。于是在stdout上,我们看到end之后又输出了一句hello。次线结束。

总结

这就是async与await的用法了。函数肚子里有需要长时等待的子函数,就给函数戴上async的帽子,并在子函数处用await等待。主线在执行时就会非阻塞地执行函数,并实际在子函数处等待。

奥妙

奥妙在于await什么函数。 await一个async函数是可以的,await一个普通函数也是可以的。 js让async函数返回值打包成一个promise,而await对promise做解包 如果await的不是一个async函数,那它对返回值不做什么。

评价

我觉得这种设计不太好。真的不好。拗口,牵强。 你真不如像mpi那样关联一个等待句柄,然后围绕句柄构建回调或者等待。