Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

javascript的执行机制 #2

Open
q837477816 opened this issue Mar 4, 2018 · 0 comments
Open

javascript的执行机制 #2

q837477816 opened this issue Mar 4, 2018 · 0 comments

Comments

@q837477816
Copy link
Owner

q837477816 commented Mar 4, 2018

js为什么是单线程的?

作为一门浏览器的脚本语言,js最早诞生是为了完成用户交互、操作DOM。如果将其设计成多线程的,那么如果同时存在两个线程,一个线程要求对某一DOM节点进行修改,而另一个线程要求删除同一DOM节点,那么浏览器该听那个线程的呢?为了避免出现这种情况,干脆一开始就将其设计成单线程的。

注意HTML5为了提高CPU的利用率而提出的Web Worker标准,允许JavaScript脚本创建多个线程,但是子线程完全受主线程控制,且不得操作DOM。所以这个标准并没有改变JavaScript单线程的本质。

单线程带来的问题

单线程意味着 同一时间只能做一件事情 ,那么存在多个任务的时候,只能按顺序执行完一个任务再接着执行下一个任务。那么倘若中间有一个任务耗时很长(比如说一个AJAX请求数据),那么后面的任务就会一直堵在那里,一直要等到AJAX请求到数据处理完后才能继续执行后面的任务。关键是CPU在等待AJAX请求数据的过程中一直是空闲的,但就是因为你这个任务没执行完,空闲的CPU也不能执行后面的任务,这样就大大浪费了效率(人家后面的任务都排队等着用你CPU,你AJAX任务还没取到数据就霸占了CPU,这不是占着茅坑不拉屎吗)。

同步任务与异步任务

为了解决向上述情况带来的CPU效率低下问题。聪明的程序员想到了可以将任务分成同步任务和异步任务。

同步任务指的是,在主线程上排队执行的任务,只有前一个任务执行完毕,才能执行后一个任务。
异步任务指的是,不进入主线程、而进入"任务队列"(task queue)的任务,只有"任务队列"通知主线程,某个异步任务可以执行了,该任务才会进入主线程执行。

js的执行机制

第一步:所有的同步任务在主线程上执行,形成一个“执行栈”。
第二步:在主线程之外还存在一个任务队列,一旦异步任务有了结果,就在任务队列里添加一个事件。
第三步:一旦执行栈中的同步任务都执行完之后,系统就会去读任务队列里面的事件,并通知对应的异步任务结束等待状态,进入执行栈中执行。

主线程不断重复上面三步,这就是所谓的Event Loop(事件循环)。

事件以及回调函数

"任务队列"是一个事件的队列(也可以理解成消息的队列),IO设备完成一项任务,就在"任务队列"中添加一个事件,表示相关的异步任务可以进入"执行栈"了。主线程读取"任务队列",就是读取里面有哪些事件。

"任务队列"中的事件,除了IO设备的事件以外,还包括一些用户产生的事件(比如鼠标点击、页面滚动等等)。只要指定过回调函数,这些事件发生时就会进入"任务队列",等待主线程读取。

所谓"回调函数"(callback),就是那些会被主线程挂起来的代码。异步任务必须指定回调函数,当主线程开始执行异步任务,就是执行对应的回调函数。

"任务队列"是一个先进先出的数据结构,排在前面的事件,优先被主线程读取。主线程的读取过程基本上是自动的,只要执行栈一清空,"任务队列"上第一位的事件就自动进入主线程。但是,由于存在后文提到的"定时器"功能,主线程首先要检查一下执行时间,某些事件只有到了规定的时间,才能返回主线程。

Promise、process.nextTick、setImmediate

Promise内的同步代码会立即执行。但是Promise.than方法指定的回调函数和process.nextTick方法可以在当前"执行栈"的尾部----下一次Event Loop(主线程读取"任务队列")之前----触发回调函数。也就是说,它俩指定的任务总是发生在所有异步任务之前。setImmediate方法则是在当前"任务队列"的尾部添加事件,也就是说,它指定的任务总是在下一次Event Loop时执行,这与setTimeout(fn, 0)很像。

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant