事件驱动模型
从setTimeout说起
这是一个JS引擎当中内置的定时器函数
官方的定义如下
setTimeout() 方法用于在指定的毫秒数后调用函数或计算表达式
但是实践证明 , 即使是setTimeout(fn, 0)
fn函数也不会立即被执行
例如下列代码
console.log(1); var timeFunc = function(){ console.log(2); } setTimeout(timeFunc,0); console.log(3);
执行的结果是1 3 2 , 而不是1 2 3
JS引擎的单线程特性
JS语言设计的一个很重要的特点就是 : JS是没有多线程的
但是JS引擎是单线程 , 浏览器却可以是多线程 , JS引擎只是浏览器的一个线程而已
定时器 网络请求 浏览器渲染等操作 , 都是由不同的线程去完成的
比如下面这个例子
var isEnd = true; window.setTimeout(function () { isEnd = false; }, 1000); while (isEnd); console.log('end');
在1s之后 , 将isEnd置为false , 表面看来后面的死循环只会持续1s而已
但是实际上这段代码会一直陷入死循环
这也证明了setTimeout并不能实现多线程
JS是基于事件驱动的语言 , 它的执行顺序遵循事件队列的机制
浏览器有各种各样的线程 , 这些线程的联系都是基于事件的 , 当JS引擎处理到与其他线程相关的代码 , 就会分发到其他的线程
在这个过程中 , JS引擎并不会阻塞自己的线程等待其他线程执行完毕 , 而且其他线程执行完毕后添加事件任务告诉js引擎执行相关操作 , 这就是js的异步编程模型
.
拿上面的定时器函数来说 , 执行这个函数的时候 , 就会开启一个定时器线程( 注意: 这个线程并不属于JS引擎 ) , 这个线程会在指定时间后向事件队列中添加一个任务 , 这个任务就是执行传递给setTimeout的函数
既然是队列 , 后入队的任务当然会在主线程的任务之后执行
如果主线程的任务一直不结束 , 那么这个队列就会一直阻塞
这同样也可以解释同步ajax请求 , 当后台响应较慢的时候造成的页面假死现象
页面要与用户进行交互 , 依靠的是响应事件 , 比如鼠标点击事件等
但是主线程任务一直不结束 , 点击事件的任务只能在后面排队 , 事件方法不会被执行 , 当然就有了假死现象
当主线程任务结束的时候 , 这些事件方法仍然会执行
由此可见官方对于settimeout的定义是有迷惑性的.应该给一个新的定义:
在指定时间内, 将任务放入事件队列,等待js引擎空闲后被执行.