来源:极链科技
作者:周哲
所谓“异步” ,简单说就是一个任务分成两段,先执行第一段,然后转而执行其他任务,等做好了准备,再回过头执行第二段。比如,有一个任务是读取文件进行处理,异步的执行过程就是下面这样。
常见的浏览器无响应(假死),往往就是因为某一段 JAVA 代码长时间运行(比如死循环),导致整个页面卡在这个地方,其他任务无法执行。
为了解决这个问题,Java 语言将任务的执行模式分成两种:同步( Synchronous )和异步( Asynchronous )。
异步编程原理
Java 引擎负责解析,执行 Java 代码,但它并不能单独运行,通常都得有一个宿主环境,一般如浏览器或 Node 服务器,前文说到的单线程是指在这些宿主环境创建单一线程,提供一种机制,调用 Java 引擎完成多个 Java 代码块的调度,这种机制就称为事件循环( Event Loop )。
关于事件循环流程分解如下:
很多的队列先后按顺序执行任务就形成了 Event
异步编程实现
1 :回调函数
优点:简单、容易理解和部署。
缺点:不利于代码的阅读和维护,各个部分之间高度耦合( Coupling ),流程会很混乱。
2 : Promise 对象
一个 promise 可能有三种状态:等待( pending )、已完成( fulfilled )、已拒绝( rejected ) ;
resolve ,接受一个成功值,传递给绑定的 fulfilled 回调函数中。主要工作是将当前状态变为 fulfilled 状态,同时调用绑定的 fulfilled 回调函数。
reject ,接受一个失败信息,传递给绑定的 rejected 回调函数中。主要工作是将当前状态变为 rejected 状态,同时调用绑定的 rejected 回调函数。
then 方法返回一个 Promise 。它有两个参数,分别为 Promise 在成功和失败情况下的回调函数。
语法:
概括来说 promise 是对异步的执行结果的描述对象。
3 : Generator
Generator 函数是 ES6 提供的一种异步编程解决方案 ,允许函数的暂停和恢复。
异步任务的封装:
整个过程类似于,浏览器遇到标识符 * 之后,就明白这个函数是生成器函数,一旦遇到 yield 标识符,就会将以后的函数放入此异步函数之内,待异步返回结果后再进行执行。
更深一步,从内存上来讲:
普通函数在被调用时,JS 引擎会创建一个栈帧,在里面准备好局部变量、函数参数、临时值、代码执行的位置(也就是说这个函数的第一行对应到代码区里的第几行机器码),在当前栈帧里设置好返回位置,然后将新帧压入栈顶。待函数执行结束后,这个栈帧将被弹出栈然后销毁,返回值会被传给上一个栈帧。
当执行到 yield 语句时, Generator 的栈帧同样会被弹出栈外,但 Generator 在这里耍了个花招 —— 它在堆里保存了栈帧的引用(或拷贝)!这样当 it.next 方法被调用时, JS 引擎便不会重新创建一个栈帧,而是把堆里的栈帧直接入栈。因为栈帧里保存了函数执行所需的全部上下文以及当前执行的位置,所以当这一切都被恢复如初之时,就好像程序从原本暂停的地方继续向前执行了。
而因为每次 yield 和 it.next 都对应一次出栈和入栈,所以可以直接利用已有的栈机制,实现值的传出和传入。