欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

我想这次我真的理解了 JavaScript 的单线程机制

程序员文章站 2022-05-04 13:46:19
今天面试的时候被问到一个问题,是关于 JS 异步的。当时我脑海中闪过了一个单线程的概念,但却没有把真正的原理阐述清楚。所以回来特意重新回顾了前面单线程和异步相关的一些知识点。 虽然之前学习的时候也接触了单线程模型相关的东西,但当时理解得并不是很清楚和明白。所以这道面试题也没有给出一语中的的答案。重新 ......

今天面试的时候被问到一个问题,是关于 js 异步的。当时我脑海中闪过了一个单线程的概念,但却没有把真正的原理阐述清楚。所以回来特意重新回顾了前面单线程和异步相关的一些知识点。

虽然之前学习的时候也接触了单线程模型相关的东西,但当时理解得并不是很清楚和明白。所以这道面试题也没有给出一语中的的答案。重新阅读阮一峰的 《javascript 运行机制详解》和我之前写的《settimeout 异步与回调》之后。我决定重新写一篇博客来更加白话的总结 js 的单线程机制和异步。

 

重演历史 - 为什么 js 是单线程

当我们打开一个页面,页面的 js 会去执行。从系统层面来说,他是单线程的去执行。换句话讲系统中只有一个线程去处理整个页面的渲染(代码的执行)。为什么会这样呢?因为简单,因为 js 很早就被发明出来了,浏览器也是。那个时候考虑的第一件事,是功能简单。试想一下如果是多线程,一个处理页面渲染 dom 操作,另外的线程也处理,就会出现混乱。因为搞不清楚谁做了什么,做到什么程度了。但如果只有一个线程单独处理这些操作,就非常好的能够管控。所以浏览器的模型就是单线程。

html5 提出 web worker 标准,允许 javascript 脚本创建多个线程,但是子线程完全受主线程控制,且不得操作 dom。所以,这个新标准并没有改变 javascript单线程的本质。

所以在早期,当使用大量同步的代码时,打开网页就会出现卡屏、卡页面的情况。因为在获取数据的时候,使用同步的代码,后面的数据还没到来之前,获取数据之后的代码都无法执行。只有等获取数据全部结束后,才会执行下面的代码。

所以后来,大家都知道了。开始使用异步。

同步队列 / 任务队列

既然是单线程,就意味着需要排队,即前面的任务结束,后面的任务才能执行。所以所有的任务被分成了两种,一种是同步任务,一种就是异步任务。同步任务指的就是在主线程上排队执行的任务,只有前一个任务执行完毕,才能继续执行后一个任务。而异步任务指的是,不进入主线程(同步队列 stack),而是进入任务队列(task queue)的任务。只有任务队列通知主线程,某个异步任务可以执行了,该任务才会进入主线程执行。

只要主线程空了,就会去读取 "任务队列" ,这就是 javascript 的运行机制。这个过程会不断重复。

 

重新理解这张图

例如我们有一段代码,这些代码是用来做一些操作。正常情况下,代码都是放置在栈(stack)中的,正常执行的时候是一个个去执行的。但是有些时候,比如给页面元素绑定一个事件,发了一条 ajax 请求,或者还有一些异步处理。这个时候,就不能够放置在 stack 中了。不然就会处于长久的等待之中,其他的代码就无法按顺序即使的执行。

所以在浏览器里面,还有一个可以被称之为任务队列的地方。对于一些异步的任务,都是放到任务队列里面。举例来说,当用户给一个元素绑定事件 onclick,绑定事件的操作在 stack 执行,但 onclick 回调本身是放到任务队列里面的。

 
我想这次我真的理解了 JavaScript 的单线程机制

 

重新理解 settimeout

之前认识 settimeout 时,就已经见过这段代码了,他的输出结果是先输出 2,后才输出 1。原理也是因为单线程机制。

settimeout(function(){
    console.log(1)
},0)

console.log(2)

即 console.log(2) 被放入到 stack 中执行,settimeout() 被放入到 callback queue了。换句话说,必须要等所有 stack 中的同步的代码全都执行完后,才会去执行异步的事件。所以不管 settimeout() 里面的时间是 0 还是 1000 都会被排到后面。



所以下面这个例子,1 永远不会被输出,且 2 被循环多次后,会被卡死。因为永远都在执行 console.log(2) 这个同步代码。而整个 stack 栈都被堵死了。 settimeout() 根本没办法执行。

var isok = true
settimeout(function(){
  console.log(1)
  isok = false
},1000)

while(isok){
  console.log(2)
}

 

 

分析原生 ajax

所以再去分析原生 ajax 里面的代码,也能够更好的理解同步任务和异步任务。

 
我想这次我真的理解了 JavaScript 的单线程机制

 

这次,我想我真的理解了 javascript 的单线程机制。

博客园大佬比较多...  由于本人水平有限。如果这篇博文中一些地方有错误,或者各位大佬觉得我还是没有理解单线程机制和异步,请轻喷...