js高级(线程机制与事件机制)

102 阅读5分钟

线程与进程

进程

  • 程序的一次执行, 它占有一片独有的内存空间
  • 可以通过windows任务管理器查看进程

线程

  • 是进程内的一个独立执行单元
  • 是程序执行的一个完整流程
  • 是CPU的最小的调度单元

关系

  • 一个进程至少有一个线程(主)
  • 程序是在某个进程中的某个线程执行的

image.png

相关知识

  • 应用程序必须运行在某个进程的某个线程上
  • 一个进程中至少有一个运行的线程: 主线程, 进程启动后自动创建
  • 一个进程中也可以同时运行多个线程, 我们会说程序是多线程运行的
  • 一个进程内的数据可以供其中的多个线程直接共享
  • 多个进程之间的数据是不能直接共享的
  • 线程池(thread pool): 保存多个线程对象的容器, 实现线程对象的反复利用

相关问题

image.png

浏览器内核模块组成

支撑浏览器运行的最核心的程序

  • 主线程

    • js引擎模块 : 负责js程序的编译与运行
    • html,css文档解析模块 : 负责页面文本的解析
    • DOM/CSS模块 : 负责dom/css在内存中的相关处理
    • 布局和渲染模块 : 负责页面的布局和效果的绘制(内存中的对象)
  • 分线程

    • 定时器模块 : 负责定时器的管理
    • DOM事件模块 : 负责事件的管理
    • 网络请求模块 : 负责Ajax请求

js线程

  1. 如何证明js执行是单线程的?

    • setTimeout()的回调函数是在主线程执行的
    • 定时器回调函数只有在运行栈中的代码全部执行完后才有可能执行

    image.png

  2. 为什么js要用单线程模式, 而不用多线程模式?

    • JavaScript的单线程,与它的用途有关。
    • 作为浏览器脚本语言,JavaScript的主要用途是与用户互动,以及操作DOM。
    • 这决定了它只能是单线程,否则会带来很复杂的同步问题—设置互斥只允许一个线程操作
  3. 代码的分类:

    • 初始化代码
    • 回调代码
  4. js引擎执行代码的基本流程

    • 先执行初始化代码: 包含一些特别的代码

      • 设置定时器
      • 绑定监听
      • 发送ajax请求
    • 后面在某个时刻才会执行回调代码

    image.png

  • js是单线程执行的(回调函数也是在主线程)
  • H5提出了实现多线程的方案: Web Workers
  • 只能是主线程更新界面

定时器问题

  1. 定时器真是定时执行的吗?

    • 定时器并不能保证真正定时执行
    • 一般会延迟一丁点(可以接受), 也有可能延迟很长时间(不能接受)(如果在主线程执行了一个长时间的操作, 可能导致延时才处理)
  2. 定时器回调函数是在分线程执行的吗?

    • 在主线程执行的, js是单线程的
  3. 定时器是如何实现的?

    • 事件循环模型(后面讲)

    image.png

事件处理机制

image.png

所有代码分类

  • 初始化执行代码: 包含绑定dom事件监听, 设置定时器, 发送ajax请求的代码
  • 回调执行代码: 处理回调逻辑

js引擎执行代码的基本流程:

  • 初始化代码===>回调代码

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

  • 事件管理模块
  • 回调队列

模型的运转流程

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

相关重要概念

image.png

H5 Web Workers

介绍

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

使用

  • 创建在分线程执行的js文件
//worker.js
var onmessage =function (event){ //不能用函数声明
    var upper= event.data;//通过event.data获得发送来的数据
    var result=upper.toUpperCase();  //处理主线程传过来的数据并返回给主线程
    postMessage( result);//将获取到的数据发送会主线程
}

image.png

  • 在主线程中的js中发消息并设置回调
//创建一个Worker对象并向它传递将在新线程中执行的脚本的URL
var worker = new Worker("worker.js");  
//接收worker传过来的已处理的数据
worker.onmessage = function (event) {     
    console.log(event.data);    //HELLO WORLD         
};
//向worker发送数据
worker.postMessage("hello world");    

image.png

image.png

应用练习

编程实现斐波那契数列(Fibonacci sequence)的计算F(0)=0,F(1)=1,..... F(n)=F(n-1)+F(n-2)

  • 直接在主线程

    响应慢,影响了页面的操作

var fibonacci =function(n) {
    return n <2 ? n : fibonacci(n -1) + fibonacci(n -2);
};
console.log(fibonacci(48));
  • 使用Worker在分线程

    • 主线程
var worker = new Worker('worker2.js');
worker.addEventListener('message', function (event) {
    var timer2 = new Date().getTime();
    console.log('结果:' + event.data, '时间:' + timer2, '用时:' + ( timer2 - timer ));
}, false);


var timer = new Date().getTime();
console.log('开始计算: ', '时间:' + timer);
setTimeout(function () {
    console.log('定时器函数在计算数列时执行了', '时间:' + new Date().getTime());
}, 1000);


worker.postMessage(40);
console.log('我在计算数列的时候执行了', '时间:' + new Date().getTime());
  • 分线程
var fibonacci =function(n) {
    return n <2 ? n : fibonacci(n -1) + fibonacci(n -2);
};


var onmessage = function(event) {
    var n = parseInt(event.data, 10);
    postMessage(fibonacci(n));
};

不足

  1. 慢(肯定是比直接在主线程运行慢)

  2. 不能跨域加载JS

  3. worker内代码不能访问DOM(更新UI)

    • 打印分线程的this

    image.png

    • 分线程中的全局对象不在是window,所以在分线程中无法更新界面,无法使用window的方法,比如alert
  4. 不是每个浏览器都支持这个新特性