简谈单线程多线程的理解
2019-11-05

JS的单线程JS语言的特点之一就是单线程,简而言之就是同一时间只能做一件事,假设JS是多线程,一个线程在某个节点上添加内容,另一个线程删除了这个节点,这时就看到了问题所在了,但是H5提出了Web Worker标准,允许js脚本创建多个线程,但是子线程完全受主线程的控制,而且你不得操作DOM,所以本质上并没有改变他单线程的本质。 这时让我们想另一个情景,有两个任务,第一个任务是ajax的请求第二个任务是操作DOM,那么第二个任务就必须等到ajax任务请求完成,但是这个任务是IO设备(输入输出设备)他非常的慢,这就使CPU闲着了,这时我们就引出了同步任务,异步任务,同步任务指在主线程上排队执行的任务只有上一个任务完成了下一个任务才执行,而异步任务指,不进入主线程,而进入‘’任务队列‘’,只有任务队列通知了主线程某个异步任务可以执行了,该任务才可以进入主进程执行     异步执行的运行机制如下    (1)所有同步任务都在主线程上执行,形成一个执行栈(execution context stack)。(2)主线程之外,还存在一个"任务队列"(task queue)。只要异步任务有了运行结果,就在"任务队列"之中放置一个事件。(3)一旦"执行栈"中的所有同步任务执行完毕,系统就会读取"任务队列",看看里面有哪些事件。那些对应的异步任务,于是结束等待状态,进入执行栈,开始执行。(4)主线程不断重复上面的第三步。主线程和任务队列的示意图如下  任务队列就是一个事件的队列,IO设备完成一项任务就向任务队列中添加一个事件,表示相关的异步任务可以进入执行栈了,主线程读取任务队列,就是读取里面有哪些事件。 任务队列中事件有:ajax异步请求,定时器,一些事件如点击事件、滚动事件等等,只要指定了回调函数,这些事件就会进入任务队列,等待主线程的读取 那么回调函数是啥呢?他就是被主线程挂起的代码,异步的任务必须指定回调函数,主线程开始执行任务队列的任务,其实就是执行回调函数 你可能会经常听到这个单词  Event Loop  他翻译过来就是 事件循环,也就是说,主线程从任务队列中读取事件,这个过程是循环不断的 主线程运行的时候,会产生堆(heap)和栈(stack),栈中的代码调用各种外部的API( Application Programming Interface) 他们会在任务队列中加入各种事件,只要栈中的代码执行完毕,主线程就会去读取任务队列,依次执行那些事件所对应的回调。 Node.js的单线程Node.js也是单线程的,但是他的运行机制不同于浏览器 他的机制如下(1)V8引擎解析JavaScript脚本。(2)解析后的代码,调用Node API。(3)libuv库负责Node API的执行。它将不同的任务分配给不同的线程,形成一个Event Loop(事件循环),以异步的方式将任务的执行结果返回给V8引擎。(4)V8引擎再将结果返回给用户。 Node.js除了setTimeout和setInterval这两个方法又新增了两个与任务队列有关的方法,process.nextTick和setImmediate process.nextTick 在下一次Event Loop(主线程读取任务之前)--触发回调函数,就是他在所有异步函数之前调用 

process.nextTick(function A() { console.log(1); process.nextTick(function B(){console.log(2);});});setTimeout(function timeout() { console.log("TIMEOUT FIRED");}, 0)// 1// 2// TIMEOUT FIRED

  

 setImmediate则是在当前任务队列的尾部添加事件,也就是说,它指定的任务总在下一次的Event Loop时执行  

setImmediate(function (){ setImmediate(function A() { console.log(1); setImmediate(function B(){console.log(2);}); }); setTimeout(function timeout() { console.log("TIMEOUT FIRED"); }, 0);});// 1// TIMEOUT FIRED// 2

  

因为A和延时器是在同一轮的Loop执行,而函数B在下一轮Loop执行由此可见,他俩一个区别是,process.nextTick 语句总是在当前执行栈一次执行完,多个setImmediate可能需要多次loop才能执行完, 1, 0, 9);