Javascript异步实现原理

一、js是单线程

首先有一点我们必须明确,js是单线程的。即暂时来说js是不可能学会周伯通左右互搏之术的,因为没法办左手画圆,右手画方。在同一时间,它只能做一件事情。这是它的核心特征之一。

二、js为什么是单线程?

那么为什么js是单线程呢?我们可以从他的用途来看,js主要运行在浏览器上,用以实现和用户之间的交互以及操作DOM。假设它是多线程的,那么可能存在这一一种情况,在某个时间他既需要在某个DOM节点上添加一些内容,又需要删除这个DOM节点。很明显这样式不合理的,所以用途决定在js诞生之初便是单线程。

三、任务队列

我们知道I/O设备的操作很慢,如果等I/O设备执行结束再执行下一个任务的话,无疑是非常差的体验,对于资源也是相当的浪费。所以js把任务分为两种,一种是同步任务,即只能等待前一个任务完成才可以进行的任务,一种是异步任务,即可以暂时等待的任务;js线程在工作的时候,异步任务会暂时进入任务队列里面,等待主线程栈里面的任务完成后再执行,或者定时到了压进主线程的执行栈。

四、事件和回调函数

任务队列是一个事件的队列,IO设备完成一项任务,就在任务队列中添加一个事件,表示相关的异步任务可以进入执行栈了。主线程读取任务队列,就是读取里面有哪些事件。
任务队列中的事件,除了IO设备的事件以外,还包括一些用户产生的事件(比如鼠标点击、页面滚动等等)。只要指定过回调函数,这些事件发生时就会进入"任务队列",等待主线程读取。
回调函数就是那些会被主线程挂起来的代码。异步任务必须指定回调函数,当主线程开始执行异步任务,就是执行对应的回调函数。
任务队列是一个先进先出的数据结构,排在前面的事件,优先被主线程读取。主线程的读取过程基本上是自动的,只要执行栈一清空,"任务队列"上第一位的事件就自动进入主线程。但是,由于定时器功能,主线程首先要检查一下执行时间,某些事件只有到了规定的时间,才能返回主线程。

五、实例

执行栈中的代码(同步任务),总是在读取任务队列(异步任务)之前执行。如下两段代码都是req.send方法Ajax操作向服务器发送数据,它是一个异步任务,意味着只有当前脚本的所有代码执行完,系统才会去读取任务队列。所以两端代码效果是一样的。

回调函数的部分(onload和onerror),在send()方法的前面或后面无关紧要,因为它们属于执行栈的一部分,系统总是执行完它们,才会去读取任务队列。

var req = new XMLHttpRequest();
req.open('GET', url);    
req.onload = function (){};    
req.onerror = function (){};    
req.send();

var req = new XMLHttpRequest();
req.open('GET', url);    
req.send();
req.onload = function (){};    
req.onerror = function (){};    


你可能感兴趣的:(js)