单线程

单线程模型指的是,JavaScript 只在一个线程上运行。也就是说,JavaScript 同时只能执行一个任务,其他任务都必须在后面排队等待。
注意,JavaScript 只在一个线程上运行,不代表 JavaScript 引擎只有一个线程。事实上,JavaScript 引擎有多个线程,单个脚本只能在一个线程上运行(称为主线程),其他线程都是在后台配合。
JavaScript 之所以采用单线程,而不是多线程,跟历史有关系。JavaScript 从诞生起就是单线程,原因是不想让浏览器变得太复杂,因为多线程需要共享资源、且有可能修改彼此的运行结果,对于一种网页脚本语言来说,这就太复杂了。如果 JavaScript 同时有两个线程,一个线程在网页 DOM 节点上添加内容,另一个线程删除了这个节点,这时浏览器应该以哪个线程为准?是不是还要有锁机制?所以,为了避免复杂性,JavaScript 一开始就是单线程,这已经成了这门语言的核心特征,将来也不会改变。

异步

JS 是单线程的,对于耗时任务如果按照顺序执行,就会导致浏览器假死卡住。所以需要异步来处理耗时任务,当任务完成后才去处理。

同步任务:在主线程上排队执行的任务,只有前一个任务执行完毕,才能执行后一个任务。

异步任务:不进入主线程,而进入任务队列中的任务,主线程完成一个事件循环空闲后,会从任务队列中读取新的任务进入主线程执行。

事件循环Event Loop:只有执行栈中的所有同步任务都执行完毕,系统才会读取任务队列,看看里面的异步任务哪些可以执行,然后那些对应的异步任务,结束等待状态,进入执行栈,开始执行。
image.png

事件循环

Javascript 是单线程的,为了在处理异步任务的时候不会发生阻塞,提出了事件循环的解决方案。从宏观上来说,主线程在处理任务时,不会等待异步任务直到返回结果,而是将异步任务挂起,继续执行其他的任务。当异步任务返回结果不会立即处理而是加入到 事件队列 中。当主线程空闲时,读取事件队列中的任务,以此循环往复就形成事件循环。

事件队列

在事件循环中分为两种任务类型:宏任务(macro task) 和 微任务(micro task)。虽然都是异步任务但是两者的优先级不同,微任务属于人民币玩家拥有VIP特权。
常见的宏任务:setIntervalsetTimeOut。微任务:Promise
两种不同的任务对应着有两种不同的任务队列:宏任务队列 和 微任务队列。在事件循环中,异步任务的返回结果会根据不同的类型,放入不同的任务队列中。当主线程空闲时,会优先查看微任务队列,如果有任务依次执行任务直到微任务队列为空。然后去读取宏任务队列中的宏任务……依次循环,直到所有任务都完成。
注意:由于微任务队列优先级高,所以同一事件循环中微任务优先执行。

  1. console.log(1);
  2. setTimeout(function () {
  3. console.log(2);
  4. new Promise(function (resolve) {
  5. console.log(3);
  6. resolve();
  7. }).then(function () {
  8. console.log(4);
  9. });
  10. });
  11. new Promise(function (resolve) {
  12. console.log(5);
  13. resolve();
  14. }).then(function () {
  15. console.log(6);
  16. });
  17. setTimeout(function () {
  18. console.log(7);
  19. new Promise(function (resolve) {
  20. console.log(8);
  21. resolve();
  22. }).then(function () {
  23. console.log(9);
  24. });
  25. });
  26. console.log(10);
  27. // 1 5 10 6 2 3 4 7 8 9