js时单线程语言
- 因为js的主要用途是【与用户互动以及操作DOM】,故js只能是单线程。
- 为了利用多核CPU的计算能力,H5提出Web Worker标准,允许js脚本创建多个线程,但是子线程完全受主线程控制,且不得操作DOM。
任务队列
- 单线程,意味着要有【任务队列】(task queue)
- 所有【任务】分为两种:
- 【同步任务】(在【主线程】上排队执行的任务,只有前一个任务执行完毕,才能执行后一个任务)
- 【异步任务】(不进入主线程,而进入任务队列的任务)
- 异步执行的运行机制
- 所有同步任务都在主线程上执行,形成一个【执行栈】;
- 主线程之外,还存在一个【任务队列】。只要异步任务有了运行结果,就在任务队列中放置一个事件;
- 一旦执行栈中的所有同步任务执行完毕,系统就会读取任务队列中的事件,与之对应的异步任务就结束等待状态,进入执行栈,开始执行;
- 主线程不断重复第三步;
【小结】:
js的运行机制:只要主线程空了,就回去读取任务队列。
事件和回调函数
- 任务队列是一个事件的队列,即消息队列。IO设备完成一项任务,就在任务队列中添加一个事件,表示相关的异步任务可以进入执行栈了。主线程读取任务队列中的事件。
- 任务队列中的事件:IO设备事件,用户操作产生的事件。
- 【回调函数】:会被主程序挂起的代码。异步任务必须指定回调函数,当主线程开始执行异步任务,就是执行对应的回调函数。
- 【任务队列】:先进先出。
Event Loop
- 主线程运行时,产生【堆】和【栈】,栈中的代码调用各种【外部API】,它们在【任务队列】中加入【事件】。只要栈中的代码执行完毕,主线程就回去读取任务队列,依次执行哪些事件所对应的【回调函数】。
- 执行栈中的代码【同步任务】,总是在读取任务队列【异步任务】之前执行。
定时器
- 任务队列中可放:异步任务的事件、定时事件
- 【定时器功能】:定时执行的代码,
setTimeout
、setTimeInterval
setTimeout(fn, t)
的含义是:指定某个任务在主线程最早可得的空闲时间执行,尽可能早地执行。它在任务队列的尾部添加一个事件,因此要等到同步任务和任务队列现有的事件都处理完,才会得到执行。- HTML5规定了
setTimeout
的time参数的最小值,若低于4毫秒,就会自动增加。另外,对于DOM的变动通常不会立即执行,而是每16毫秒执行一次。 setTimeout()
只是将事件插入了任务对垒,必须等到当前代码执行栈执行完,主线程才会去执行它指定的回调函数。若当前代码耗时很长可能要等很久,所以并不能保证回调函数一定会在setTimeout()指定的时间执行。
Nodejs环境下
执行过程:
- V8引擎解析js脚本
- 解析后的代码,调用Node API
- libuv库负责Node API的执行。它将不同的任务分配给不同的线程,形成一个Event Loop(【事件循环】),以异步的方式将任务的执行结果返回给V8引擎
- V8引擎再将结果返回给用户
(未完待续)