zoukankan      html  css  js  c++  java
  • 对微任务和宏任务的执行顺序的个人理解

    首先我们先看看同步与异步的定义,及浏览器的执行机制,方便我们更好地理解同步异步编程。
      
      浏览器是多线程的,JS是单线程的(浏览器只分配一个线程来执行JS)
     
      进程大线程小:一个进程中包含多个线程,例如在浏览器中打开一个HTML页面就占用了一个进程,加载页面的时候,浏览器分配一个线程去计算DOM树,分配其它的线程去加载对应的资源文件...再分配一个线程去自上而下执行JS

      同步:在一个线程上(主栈/主任务队列)同一个时间只能做一件事情,当前事情完成才能进行下一个事情(先把一个任务进栈执行,执行完成,在把下一个任务进栈,上一个任务出栈...)

      异步:在主栈中执行一个任务,但是发现这个任务是一个异步的操作,我们会把它移除主栈,放到等待任务队列中(此时浏览器会分配其它线程监听异步任务是否到达指定的执行时间),如果主栈执行完成,监听者会把到达时间的异步任务重新放到主栈中执行...
        
      [宏任务:macro task]
            - 定时器
            - 事件绑定
            - ajax
            - 回调函数
            - Node中fs可以进行异步的I/O操作
      [微任务:micro task]
            - Promise(async/await)  => Promise并不是完全的同步,当在Excutor中执行resolve或者reject的时候,此时是异步操作,会先执行then/catch等,当主栈完成后,才会再去调用resolve/reject把存放的方法执行
            - process.nextTick (node中实现的api,把当前任务放到主栈最后执行,当主栈执行完,先执行nextTick,再到等待队列中找)
       - MutationObserver   (创建并返回一个新的 MutationObserver 它会在指定的DOM发生变化时被调用。)
     

      执行顺序优先级:SYNC => MICRO => MACRO

      所有JS中的异步编程仅仅是根据某些机制来管控任务的执行顺序,不存在同时执行两个任务这一说法

    先来看一个例子:

    复制代码
    setTimeout(() => {
        console.log(1);
    }, 20);
    
    setTimeout(() => {
        console.log(2);
    }, 0);//=>默认会有最小的等待时间(V8一般是5~6MS)
    
    console.time('WHILE');
    let i = 0;
    while (i <= 99999999) {
        i++;
    }
    console.timeEnd('WHILE');
    
    setTimeout(() => {
        console.log(3);
    }, 10);
    
    console.log(4);
    复制代码

     结果输出如图:

    我们先模拟下浏览器的程序执行过程,代码自上而下执行,碰到第一个程序,先放入主栈(主任务队列),此时浏览器发现这是一个宏任务定时器,把它移出主栈,放入等待任务队列,再继续执行下面的代码,放入主栈执行,发现第二个任务也是宏任务的定时器,放入等待队列,继续往下执行,推入主栈,同步任务,循环99999999次之后输出次数,再执行下一个程序,也移入等待队列,再执行代码,发现是同步任务,输出4,此时主栈空闲,任务队列到达时间后先进先出的原则,首先第二个任务到达时间,把它放入主栈执行,输出2,此时本因输出3,因为第三个程序是10ms到达,第一个是20s到达,但是第三个程序是等待247.849853515625ms后才放入的等待队列,所以第一个程序先到达,输出1,最后输出3。

     

    我们用ajax来看看js的同步与异步的执行顺序和机制,AJAX任务开始:SEND,AJAX任务结束:状态为4
    复制代码
    let xhr = new XMLHttpRequest();
    xhr.open('GET', 'xxx.txt', false);
    // 放到等待区的时候,此时状态是1
    xhr.onreadystatechange = () => { 
        console.log(xhr.readyState);//=>4
    };
    xhr.send();
    // 同步ajax,xhr.send时为同步,xhr.send()执行完后状态为4,任务状态为4的时候主栈空闲,onreadystatechange监听到状态变化,输出4
    复制代码
    复制代码
     let xhr = new XMLHttpRequest();
     xhr.open('GET', 'xxx.txt', false);
     xhr.send();
    // 状态已经为4了
     xhr.onreadystatechange = () => {//=>状态改变才会触发,放到等待区的时候状态已经为4了,不会在改变了,所以不会执行这个方法(啥都不会输出)
         console.log(xhr.readyState);
     };
    复制代码
    复制代码
     let xhr = new XMLHttpRequest();
     xhr.open('GET', 'xxx.txt');
     xhr.send();//=>异步操作:执行SEND后,有一个线程是去请求数据,主栈会空闲下来
    // 放等待区之前状态是1
     xhr.onreadystatechange = () => {
         console.log(xhr.readyState);//=> 2 3 4
     };
    // 主栈又空闲了
    // 状态为2  把函数执行
    // 状态为3  把函数执行
    // 状态为4  把函数执行
    复制代码
  • 相关阅读:
    inclusion_tag 组件
    自定制插件widget 组件
    Python的学习之旅———logging模块
    Python的学习之旅———面向过程
    Python的学习之旅———迭代器 生成器
    Python的学习之旅———模块与包的使用 常用模块
    Python的学习之旅———函数的递归调用 匿名函数 内置函数
    Python的学习之旅———三元表达式 列表解析 序列化
    Python的学习之旅———函数对象、函数嵌套、名称空间与作用域、装饰器
    Python的学习之旅———名称空间与作用域
  • 原文地址:https://www.cnblogs.com/chenhuichao/p/15209298.html
Copyright © 2011-2022 走看看