线程机制与事件机制②

导读:本篇文章讲解 线程机制与事件机制②,希望对大家有帮助,欢迎收藏,转发!站点地址:www.bmabk.com,来源:原文

也许你感觉自己的努力总是徒劳无功,但不必怀疑,你每天都离顶点更进一步。今天的你离顶点还遥遥无期。但你通过今天的努力,积蓄了明天勇攀高峰的力量。加油!

JS是单线程执行的

我们可以用以下代码证明JS是单线程执行的:

<script type="text/javascript">
  setTimeout(function () {
    console.log('timeout 2222')
    alert('22222222')
  }, 2000)
  setTimeout(function () {
    console.log('timeout 1111')
    alert('1111111')
  }, 1000)
  setTimeout(function () {
    console.log('timeout() 00000')
  }, 0)
  function fn() {
    console.log('fn()')
  }
  fn()

  console.log('alert()之前')
  alert('------') //暂停当前主线程的执行, 同时暂停计时, 点击确定后, 恢复程序执行和计时
  console.log('alert()之后')
</script>

alert()函数有暂停当前主线程的执行, 同时暂停计时的功能

有运行结果可以知道:
setTimeout()的回调函数是在主线程执行的,定时器回调函数只有在运行栈中的代码全部执行完后才有可能执行。

为什么js要用单线程模式, 而不用多线程模式?
JavaScript的单线程,与它的用途有关。作为浏览器脚本语言,JavaScript的主要用途是与用户互动,以及操作DOM。这决定了它只能是单线程,否则会带来很复杂的同步问题。

例如我们可以想象一下:我们现在有两个线程对一个div进行操作,其中一个线程是设置样式的,另一个线程是删除的。有这么一种情况它是有发生的可能性的:当运行其中一个线程进行样式设置时,突然线程切换,把div删除了,当回到原来的线程的时候就会出现错误。

我们可以将JS中的代码从另一种角度分类:

  • 初始化代码
  • 回调代码

js引擎执行代码的基本流程:先执行初始化代码: 包含一些特别的代码 –> 回调函数(异步执行,后面在某个时刻才会执行回调代码)

常见的回调函数:

  • 设置定时器
  • 绑定事件监听
  • 发送ajax请求

特别注意:setTimeout()是初始化代码,它里面的回调函数才是回调代码!

事件循环模型

原理图:
在这里插入图片描述
WEB APIS中的三个模块负责处理对应的回调代码。从此图我们可以明显的发现这三个模块处理器(由浏览器提供)是处于分线程的位置,而JS引擎处于主线程的位置(它也只可能处于主线程的位置)

模型的2个重要组成部分:

  • 事件(定时器/DOM事件/Ajax)管理模块
  • 回调队列(callback queue)

模型的运转流程:
执行初始化代码, 将事件回调函数交给对应模块管理。当事件发生时, 管理模块会将回调函数及其数据添加到回调列队中,只有当初始化代码执行完后(可能要一定时间), 才会遍历读取回调队列中的回调函数执行。

相关概念:

  • 执行栈(execution stack):所有的代码都是在此空间中执行的
  • 任务队列(task queue)
  • 消息队列(message queue)
  • 事件队列(event queue)
  • 上面三个队列等同于回调队列(callback queue)
  • 事件轮询:从任务队列中循环取出回调函数放入执行栈中处理(一个接一个)

此时我们可以解释在线程机制与事件机制①中为什么定时器不准的原因。真正帮我们计时的是setTimeout回调处理模块,JS引擎是不会帮我们计时的。在时间到了之后并不意味着马上执行而是模块把回调函数放入到回调队列中,这个时候处于一个等待执行的状态。只有在执行栈中的代码执行完了以后,才会把回调队列中的代码放到执行栈中去执行。当执行栈中的初始化代码所花时间过长的时候,我们的回调代码会晚进入执行栈,就给我们计时不准的错觉。

H5 Web Workers实现多线程

Web Workers 是 HTML5 提供的一个javascript多线程解决方案。我们可以将一些大计算量的代码交由web Worker运行而不冻结用户界面。但是子线程完全受主线程控制,且不得操作DOM所以,这个新标准并没有改变JavaScript单线程的本质

使用步骤:

  • 创建在分线程执行的js文件
  • 在主线程中的js中发消息并设置回调

相关API

  • Worker: 构造函数, 加载分线程执行的js文件
  • Worker.prototype.onmessage: 用于接收另一个线程的回调函数
  • Worker.prototype.postMessage: 向另一个线程发送消息

我们模拟一个场景:用分线程计算斐波那契数列,在计算的这段时间之内不冻结用户界面,可以自由操作。

主线程:

<input type="text" placeholder="数值" id="number">
<button id="btn">计算</button>
<script type="text/javascript">
  var input = document.getElementById('number')
  document.getElementById('btn').onclick = function () {
    var number = input.value

    //创建一个Worker对象
    var worker = new Worker('worker.js')
    // 绑定接收消息的监听
    worker.onmessage = function (event) {
      console.log('主线程接收分线程返回的数据: '+event.data)
      alert(event.data)
    }

    // 向分线程发送消息
    worker.postMessage(number)
    console.log('主线程向分线程发送数据: '+number)
  }
  // console.log(this) // window

</script>

分线程:

function fibonacci(n) {
  return n<=2 ? 1 : fibonacci(n-1) + fibonacci(n-2)  //递归调用
}

console.log(this)
this.onmessage = function (event) {
  var number = event.data
  console.log('分线程接收到主线程发送的数据: '+number)
  //计算
  var result = fibonacci(number)
  postMessage(result)
  console.log('分线程向主线程返回数据: '+result)
  // alert(result)  alert是window的方法, 在分线程不能调用
  // 分线程中的全局对象不再是window, 所以在分线程中不可能更新界面
  //console是每个浏览器都提供的实现,跟window没有关系
}

注意:

  • alert是window的方法, 在分线程不能调用
  • 分线程中的全局对象不再是window, 所以在分线程中不可能更新界面
  • console是每个浏览器都提供的实现,跟window没有关系

图解:
在这里插入图片描述
不足之处:

  • 不能跨域加载JS
  • worker内代码不能访问DOM(更新UI)
  • 不是每个浏览器都支持这个新特性

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

文章由极客之音整理,本文链接:https://www.bmabk.com/index.php/post/122206.html

(0)
飞熊的头像飞熊bm

相关推荐

发表回复

登录后才能评论
极客之音——专业性很强的中文编程技术网站,欢迎收藏到浏览器,订阅我们!