分享我看过的比较好的文章,大家可以互相学习。如果大家有好的文章,也可以留下链接,互相学习。非常感谢!线程和进程线程和进程的关系可以用下图来说明:一个进程就像图中的一个工厂,有自己的工厂资源。线程就像图中的工人。多个工人在一个工厂里一起工作,工厂和工人之间是1:n的关系。多个工厂独立存在。官方的说法是:进程是CPU资源分配的最小单位。线程是CPU调度的最小单位。从一个更直观的例子来说,你可以打开任务管理器来查看。第一个选项卡是进程列表,直观的展示了各个进程占用CPU资源和内存资源的比例。为什么js是单线程的?刚开始学习计算机语言的时候,不管是C、C++还是JAVA,都支持多线程,而JavaScript是单线程的,不支持多线程。这也与JavaScript的作用有关。我们都知道JavaScript是主要运行在浏览器的脚本语言,最终操作的是页面的DOM结构。当两个JavaScript脚本同时修改页面的同一个DOM节点时,浏览器应该执行哪一个?所以当时在设计JavaScript的时候,要求当前的修改操作完成后,才能进行下一次的修改操作。浏览器支持多进程。同样,我们打开浏览器的任务管理器。下图为例:浏览器的每个标签页都是一个进程,有对应的内存占用、CPU占用率、进程ID。当打开一个新的标签页时,会创建一个新的进程,所以有一个标签页对应一个进程的说法,但是这个说法是错误的,因为浏览器有自己的优化机制,当我们打开多个空白标签页时页面,浏览器会将这多个空白页面的进程合并为一个,从而减少进程数。浏览器内核浏览器内核中有多个进程同步工作。今天涉及到的浏览器进程主要有以下几个进程:浏览器进程主进程主要负责页面管理以及其他进程的创建和销毁。常驻Threads包括:GUI渲染线程JS引擎线程事件触发线程定时器触发线程HTTP请求线程GUI渲染线程主要负责页面渲染、解析HTML、CSS、构建DOM树、布局绘制等,??该线程会被执行当界面需要重绘或某些操作触发回流时。该线程与JS引擎线程互斥。JS引擎线程执行时,会暂停GUI渲染。当任务队列空闲时,JS引擎将执行GUI渲染。JS引擎线程这个线程当然主要负责处理JavaScript脚本,执行代码。它还主要负责执行准备执行的事件,即当定时器计数结束,或者异步请求成功并正确返回时,依次进入任务队列,等待JS的执行引擎线程。当然,这个线程和GUI渲染线程是互斥的。当JS引擎线程执行JavaScript脚本的时间过长,会导致页面渲染被阻塞。事件触发线程主要负责将准备好的事件传递给JS引擎线程执行。例如当setTimeout定时器计时结束,ajax等异步请求成功并触发回调函数,或者当用户触发点击事件时,线程会依次将ready-to-go事件添加到任务尾部队列,等待JS引擎线程执行。顾名思义,定时器触发线程是负责执行异步定时器等函数的线程,如:setTimeout、setInterval。主线程在顺序执行代码的时候,遇到定时器,就会把定时器交给线程处理。计数完成后,事件触发线程会将计数的事件添加到任务队列的尾部,等待JS引擎线程执行。顾名思义,HTTP请求线程负责执行异步请求和其他功能,如:Promise、anxios、ajax等。当主线程依次执行代码时,遇到异步请求,会递交把函数交给线程进行处理。当检测到状态码变化时,如果有回调函数,事件触发线程会将回调函数添加到任务队列的尾部,等待JS引擎线程执行。多个线程相互协作,各司其职。渲染进程浏览器渲染进程(浏览器内核)主要负责页面渲染、JS执行和事件循环。同步任务和异步任务同步任务是可以立即执行的任务,比如console.log()打印日志,声明变量,或者执行加法操作等。异步任务与事件任务相反不会立即执行。异步任务包括宏任务和微任务(稍后解释~)。常见的异步操作:AjaxDOM的事件操作setTimeoutPromise的then方法Node的读取文件下图是同步任务和异步任务的执行流程:栈就像一个容器,任务在栈上执行。主线程就像一个操作员,负责执行栈上的任务。任务队列就像等待处理的项目。异步任务注册后,会在任务队列中加入回调函数等待主线程执行。执行栈中的同步任务执行完毕后,会检查并读取任务队列中的事件函数,任务队列中的函数结束等待状态,进入执行栈,开始执行。那么任务是如何被压入和压出栈的呢?可以用一小段代码来解释。推送和弹出以下面的代码为例:console.log(1);函数fn1(){console.log(2);}functionfn2(){console.log(3);fn1();}setTimeout(function(){console.log(4);},2000);fn2();控制台日志(5);所以上面代码运行的结果是:1,3,2,5,4。宏任务和微任务异步任务分为宏任务和微任务,宏任务队列可以有多个,微任务队列只有一个。宏任务和微任务在浏览器和Node.js中的执行方式不同。macrotask(宏任务)脚本(全局任务)、setTimeout、setInterval、setImmediate、I/O、UI渲染microtask(宏任务)process.nextTick、Promise.then()、Object.observe、MutationObserver在microtaskprocess.nextTick中优先级较高比诺言。当一个异步任务入栈时,主线程判断该任务为异步任务,将任务交给异步处理模块处理。当异步处理模块处理完毕,触发条件命中时,根据任务类型,将回调函数推送到任务队列中。如果是宏任务,添加一个宏任务队列,任务队列中的宏任务可以有多个来源。如果是微任务,则直接推入微任务队列。所以上图中的任务队列可以进一步细化:宏任务和微任务在栈为空时的执行机制是什么?EventLoop至此,除了上面的问题,我们已经搞清楚了事件循环最基本的处理方式,但是还没有搞清楚异步任务中的宏任务和微任务。我们可以先过一遍执行机制:从全局任务脚本开始,任务依次入栈,由主线程执行,执行完出栈。当遇到异步任务时,交给异步处理模块处理,相应的异步处理线程处理异步任务需要的操作,如定时器计数、异步请求监听状态变化等。当异步任务到达可执行状态时,事件触发线程将回调函数添加到任务队列中,等待栈为空时,依次入栈执行。问题来了,异步任务入栈执行时,是宏任务还是微任务?由于执行代码入口是全局任务脚本,而全局任务是宏任务,当栈为空执行同步任务时,会优先执行微任务队列中的任务。microtask队列中的任务全部执行完后,会读取macrotask队列中的top任务。在macrotasks的执行过程中,会遇到microtasks,依次加入到microtasks队列中。栈为空后,再次读取微任务队列中的任务,以此类推。实例分析回到第一段代码,现在我们可以一步步看执行顺序。console.log(1);setTimeout(function(){console.log(2);},0);setTimeout(function(){console.log(3)},2000)console.log(4);从全局Task入口,先打印log1,遇到宏任务setTimeout,交给异步处理模块,我们暂时记录为setTimeout1,再次遇到宏任务setTimeout,交给异步处理模块,我们先记录为setTimeout2,依次执行,打印log4。此时,同步任务已经执行完毕。读取宏任务队列中的任务,先执行setTimeout1的回调函数。因为定时器的等待时间是0秒,所以会直接输出2,但是W3C在HTML标准中规定,setTimeout中规定时间间隔小于4ms的都算4ms。由于浏览器执行以上三步的时间并不长,执行宏任务setTimeout1时,setTimeout2的等待时间还没有结束,所以在2秒后打印出Log3,实际上并没有等待2秒。让我们看另一个例子:setTimeout(function(){console.log(1);Promise.resolve().then(function(){console.log(2)})},0)setTimeout(function(){console.log(3)},0)Promise.resolve().then(function(){console.log(4)});console.log(5)代码中遇到异步请求事件时,应该如何执行,按照上面总结的执行机制应该得到什么样的结果?第一轮循环也是从全局任务入口开始,遇到宏任务setTimeout,交给异步处理模块处理。我们暂且记录为setTimeout1。由于等待时间为0,所以直接加入到宏任务队列中。又遇到宏任务setTimeout,交给异步处理模块处理。我们暂且记录为setTimeout2,同样直接加入到宏任务队列中。遇到microtaskthen(),加入microtask队列。最后遇到print语句,直接打印log5。第一轮循环结束后,可以画出下图:第二轮循环栈为空后,先执行microtask队列中的then()方法,输出4,此时microtask队列为空.读取宏任务队列的顶部任务setTimeout1。先直接执行print语句,打印log1,然后遇到microtaskthen(),加入microtask队列。第二个循环结束。第三个循环,首先执行microtask队列中的then()方法,输出2。此时微任务队列为空。继续阅读宏任务队列的置顶任务setTimeout2。直接执行print语句,打印日志3。第三次循环结束,执行完毕。最后,我们是我们的老板。欢迎大家在评论区留言,把你心中的正确答案写下来。console.log(1);setTimeout(function(){console.log(2);newPromise(function(resolve,reject){console.log(3);resolve();}).then(function(){console.log(4);})})newPromise(function(resolve,reject){console.log(5);resolve();}).then(function(){console.log(6);})setTimeout(function(){console.log(7)})setTimeout(function(){console.log(8);newPromise(function(resolve,reject){console.log(9);resolve();}).then(function(){console.log(10);})})newPromise(function(resolve){console.log(11);resolve();}).then(function(){console.log(12)})console.log(13)github地址:https://github.com/ABCDdouyaer/a_article_per_day/tree/master/0001
