关于javascript:笔记JS异步编程单线程定时器

同步与异步

同步

调用之后失去后果,再执行其余工作

const test = () => {
  let t = +new Date();
  while (true) {
    if (+new Date() - t >= 2000) {
      break;
    }
  }
};
console.log(1);
test();
console.log(2);
console.log(3);
// 输入:1 
// 执行test()占用2s,2s后输入 2 3

异步

调用之后不论后果,继续执行其余工作

console.log(1);
setTimeout(() => {
  console.log(2);
}, 2000);
console.log(3);
// 输入:1 3
// 定时器2秒后输入 2

过程与线程

材料:过程与线程的简略解释

过程

程序运行的实例
同一程序能够产生多个过程
一个过程蕴含一个或多个线程

线程

操作系统可能进行运算调度的最小单位
一次只能执行一个工作
有本人的调用栈、寄存器环境
同一过程的线程共享过程资源

查看过程的罕用linux命令:

查看过程的状态:`ps (process status)`
查看动静的过程变动:`top (table of processes)`

JS单线程

浏览器的过程

启动浏览器后,会产生多个过程

材料:
古代浏览器的多过程架构
浏览器的过程和线程

渲染过程

渲染过程包含:

  • GUI线程 渲染布局,解析HTML、CSS,构建DOM树
  • JS引擎线程

    • 解析执行JS(Chrome V8就是JS引擎,跑在JS引擎线程,JS引擎线程只有一个。因为解释JS的引擎是单线程的,所以称JS为单线程)
    • 与GUI线程互斥(因为JS能够操作DOM,如果和GUI线程同时操作DOM,就会出问题)
  • 定时器触发线程

    • setTimeout
    • setInterval
  • 事件触发线程

    • 将满足条件的事件放入工作队列
  • 异步HTTP申请线程

    • XHR所在线程 (解决ajax申请,申请实现后,如果有回调就告诉事件触发线程往工作队列增加事件)

JS通过浏览器内核的多线程实现异步

异步场景:

  1. 定时器
  2. 网络申请
  3. 事件绑定
  4. Promise

定时器

定时器工作流程

  1. 调用WebAPI,如setTimeout
  2. 定时器线程计数
  3. 事件触发线程将定时器事件放入工作队列
  4. 主线程通过Event Loop遍历工作队列

代码剖析:

console.log(1);
setTimeout(() => {
  console.log(2);
}, 2000);
console.log(3);
执行过程:
console.log(1) 同步工作入栈执行,执行完出栈,打印1
调用setTimeout,定时2s
console.log(3) 同第一步,打印3
执行栈空,查看工作队列,不到2s还没有工作
2s到,事件触发线程将工作增加进工作队列
循环查看就会发现有工作,将工作放入执行栈执行,打印2

定时器可能存在的问题

  1. 定时工作可能不会按时执行

    // 因为同步工作执行了5s,所以5s后才打印2
    const test = () => {
      let t = +new Date();
      while (true) {
        if (+new Date() - t >= 5000) {
          break;
        }
      }
    };
    setTimeout(() => {
      console.log(2);
    }, 2000);
    test();
  2. 定时器嵌套5次后最小距离不能低于4ms(不同浏览器实现不同)

定时器利用场景

  • 防抖
  • 节流
  • 倒计时
  • 动画(存在丢帧的状况)学习requestAnimationFrame

setTimeout代码执行剖析

for (var i = 1; i <= 10; i++) {
  setTimeout(function() {
    console.log(i);
  }, 1000 * i);
}

setTimeout等同步的for循环执行后才执行,此时i曾经变成11,所以后果是每隔1s打印一个11,打印10个11

  1. 能够利用闭包造成作用域,保留i的值,能够实现打印每隔1s打印1-10

      for (var i = 1; i <= 10; i++) {
        (function (x) {
          setTimeout(() => {
            console.log(x)
          }, 1000 * x)
        })(i)
      }
  2. 因为var没有块级作用域,所以换成ES6的let也能够实现

    for (let i = 1; i <= 10; i++) {
      setTimeout(function() {
        console.log(i);
      }, 1000 * i);
    }

评论

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

这个站点使用 Akismet 来减少垃圾评论。了解你的评论数据如何被处理