来自FIBJS核心贡献者陈垒在 D2 的演讲 “FIBJS模块重构—从回调到协程”。
JS的应用非常广泛,例如做一些浏览器的发展、机器学习、控制机器人以及编写嵌入式的应用。
如上图所示为使用浏览器原生的对象发送一个请求的典型例子。首先创建了一个xhr对象,接下来的一步并不是马上发送它,而是要设一个回调函数onreadystatechange,这个函数表示请求完成以后怎样处理返回结果。xhr也定义了本次请求要发送的HTTP动词和发送的目标网址,直到最后一步才真正将请求发送出去。但是,这个代码是异步的,当send运行之后,代码继续执行,很可能代码没有完成,等到请求完成后才可以处理。像xhr的请求,它的异步过程的执行顺序和声明顺序不一致,先定义回调再发送请求,这种情况称之为异步。因此,要求执行顺序和声明顺序严格一致。现代的应用不是单纯的同步或者异步,往往是两者的混合,时而同步时而异步,所以如何按需处理同步和异步的流程一直是一个热门问题。
也可以使用Promise把异步流程转换到同步,如上图所示。浏览器首先提供了一个fetch方法,发起一个fetch请求,通过then方法将回调处理,推到一个队列里。如果在回调里请求是正确的,返回了HTTP,那么会运行then,如果HTTP请求出错,则会运行catch。但是发完请求之后还是会继续向下执行,对所有的返回结果的处理还是要到then里执行,而上图中的代码是没办法依赖then里的返回结果的,因为不知道什么时候才会执行完成,所以会出现异步的传染性问题,一旦使用回调或promise的方式,能处理异步的方法就变得局限了,只能在then或catch里处理回调的过程。
那么使用async/await + Promise来控制异步会产生什么效果,如上图所示。首先是一个request请求,然后在函数上标记一个async方法,它依然会发起一个fetch请求,与上述唯一不同的是在fetch前加了一个await,这样,在fetch里无论是成功还是失败,总可以得到一个值,这个值会存到result里,当result有值时才会执行。这种方式即处理了异步又可以阻塞上面的流程。但还是会出现异步的传染性问题,await不能用在JS脚本的全局中。