1. JavaScript是单线程,非阻塞的
单线程
- JavaScript的主要用途是与用户互动,以及操作DOM。如果它是多线程的会有很多复杂的问题要处理,比如有两个线程同时操作DOM,一个线程删除了当前的DOM节点,一个线程是要操作当前的DOM阶段,最后以哪个线程的操作为准?为了避免这种,所以JS是单线程的。
非阻塞:通过 event loop 实现。
2.同步和异步任务
- JS中所谓的任务包括执行JS代码、对用户的输入(如:鼠标点击、键盘输入)作出反应、处理异步的网络请求等等,按照执行方式可以分为分为同步任务和异步任务,现在我们清楚的知道浏览器的一个渲染进程只分配给js一个主线程(JS引擎线程),用来执行代码,这意味着所有的任务最终都要进入主线程通过调用栈来执行。
- 同步任务
同步任务指的是在主线程上排队执行的任务,只有前一个任务执行完毕,才能执行后一个任务。 - 异步任务
异步任指的是不会进入主线程,而是被放到任务队列 (task queue)中排队等待执行,当主线程的任务全部执行完,主线程会从"任务队列"中读取事件执行代码,这个过程是循环不断的,所以整个的这种运行机制又称为事件循环(Event Loop)
Promise本身是同步的立即执行函数,.then是异步执行函数
3. 宏任务和微任务
- 为什么要引入微任务
页面渲染事件,各种IO的完成事件等随时被添加到任务队列中,一直会保持先进先出的原则执行,我们不能准确地控制这些事件被添加到任务队列中的位置。但是这个时候突然有高优先级的任务需要尽快执行,那么一种类型的任务就不合适了,所以引入了微任务队列。 - 宏任务
由宿主环境(这里指浏览器)提供的叫宏任务,如上边提到的setTimeout、网络请求Ajax、用户I\O(输入输出)。
包含:script(整体代码), setTimeout, setInterval, I/O, UI rendering; - 微任务
由ECMAScript提供的叫微任务,如ES6提供的Promise 。
包含: Promise, Object.observe
- 宏任务有宏任务队列
- 微任务有微任务队列
- js 一次只能执行一个宏任务
- 执行完第一个宏任务,准备执行第二个宏任务的时候要看看微任务队列中有没有可执行的微任务,如果有,优先将微任务执行完成
4.浏览器的事件循环
- 在当前执行栈为空时,主线程会查看微任务队列是否有事件存在
- 存在,依次执行队列中的事件对应的回调,直到微任务队列为空,然后去宏任务队列中取出最前面的事件,把当前的回调加到当前指向栈。
- 如果不存在,那么再去宏任务队列中取出一个事件并把对应的回到加入当前执行栈;
- 当前执行栈执行完毕后时会立刻处理所有微任务队列中的事件,然后再去宏任务队列中取出一个事件。同一次事件循环中,微任务永远在宏任务之前执行。
- 执行一个宏任务(栈中没有就从事件队列中获取)
- 执行过程中如果遇到微任务,就将它添加到微任务的任务队列中
- 宏任务执行完毕后,立即执行当前微任务队列中的所有微任务(依次执行)
- 当前宏任务执行完毕,开始检查渲染,然后GUI线程接管渲染
- 渲染完毕后,JS线程继续接管,开始下一个宏任务(从事件队列中获取)
注意:如果在执行微任务的过程中,又产生了微任务,那么会加入到队列的末尾,也会在这个周期被调用执行。
-
异步代码的执行,遇到异步事件不会等待它返回结果,而是将这个事件挂起,继续执行执行栈中的其他任务。当异步事件返回结果,将它放到事件队列中,被放入事件队列不会立刻执行起回调,而是等待当前执行栈中所有任务都执行完毕,主线程空闲状态,主线程会去查找事件队列中是否有任务,如果有,则取出排在第一位的事件,并把这个事件对应的回调放到执行栈中,然后执行其中的同步代码。
https://blog.csdn.net/dreamingbaobei3/article/details/89520853
https://zhuanlan.zhihu.com/p/145383822