zoukankan      html  css  js  c++  java
  • js基础-node环境下说哈js异步,同步,单线程,微任务和宏任务

    js执行环境是‘单线程’ 

    什么是单线程(single thread)?

    指一次只能完成一件任务。

    如果有多个任务,就必须排队,前面一个任务完成,再执行后面一个任务,以此类推

    粗暴理解:【某工厂只有一条生产流水线,做啥都的一个一个排着弄】

     

    好处:实现起来比较简单,执行环境相对单纯;

    坏处:只要有一个任务耗时很长,后面的任务都必须排队等着,会拖延整个程序的执行。

    常见的浏览器无响应(假死),往往就是因为某一段Javascript代码长时间运行(比如死循环),导致整个页面卡在这个地方,其他任务无法执行。

     

    为了解决单线程这问题,Javascript语言将任务的执行模式分成两种:同步(Synchronous)和异步(Asynchronous)

     

    同步模式:

    1)前一个任务完成后,执行下一个任务。

    2)程序的执行顺序与任务的排列顺序是一致的、同步的;

    粗暴理解:【同一时间之内干一件事】

     

    异步模式:

    1)前一个任务结束后,不是执行后一个任务,而是执行回调函数,

         后一个任务不等前一个任务结束就执行

    2)程序的执行顺序与任务的排列顺序是不一致的、异步的

    粗暴理解:【同一时间,因为某事执行需要时间,就空出来先干别的事】

     

    简言之:同步会堵塞代码执行,而异步不会。

     

    微任务和宏任务概念

    1. 宏任务:当前调用栈中执行的代码成为宏任务。 

    2.微任务: 当前(此次事件循环中)宏任务执行完,在下一个宏任务开始之前需要执行的任务,可以理解为回调事件。

    3. 宏任务中的事件放在callback queue中,由事件触发线程维护;微任务的事件放在微任务队列中,由js引擎线程维护。

    微任务和宏任务运行机制

    1. 在执行栈中执行一个宏任务。 

    2. 执行过程中遇到微任务,将微任务添加到微任务队列中。

    3. 当前宏任务执行完毕,立即执行微任务队列中的任务。 

    4. 当前微任务队列中的任务执行完毕,检查渲染,GUI线程接管渲染。 

    5. 渲染完毕后,js线程接管,开启下一次事件循环,执行下一次宏任务(事件队列中取)。

    微任务:process.nextTick、MutationObserver、Promise.then、process.nextTick、 catch finally

    宏任务:I/O、setTimeout、setInterval、setImmediate、requestAnimationFrame  、new Promise、async

    process.nextTick() > Promise.then() > setTimeout > setImmediate;

    运行方式:先同步任务 再异步任务!

         先宏任务  再微任务!      

    【同步任务时,先执行同步宏任务再执行同步微任务,同步操作完成后,按照先后顺序执行异步操作,先执行异步宏任务再执行异步微任务,若再遇到异步或微任务就抛出就抛出】

    注:Promise本身是同步的立即执行函数, 当在executor中执行resolve或者reject的时候, 此时是异步操作, 会先执行then/catch等,当主栈完成后,才会去调用resolve/reject中存放的方法执行,

       async 函数返回一个 Promise 对象,当函数执行的时候,一旦遇到 await 就会先返回(如返回awaite紧跟的函数),然后再阻塞

    例子1:

     1 setTimeout(function() {
     2     console.log('setTimeout');
     3 })
     4 
     5 new Promise(function(resolve) {
     6     console.log('promise');
     7     //resolve();
     8 }).then(function() {
     9     console.log('then');
    10 })
    11 
    12 console.log('console');

     结果:promise console setTimeout

    说明:

    1 整段代码作为宏任务进入主线程
    2 遇到settimeout,将其回调函数注册后分发到宏任务Event Queue。
    3 遇到了Promise,new Promise立即执行,then函数分发到微任务Event Queue
    4 遇到console.log(),立即执行
    5 第一个宏任务执行结束,看看有什么微任务,发现有then,执行 
    6 第二轮循环,发现宏任务settimeout的回调函数,执行。
    7 结束。

    例子2:

     console.log("1"); //第一轮主线程【1】 
    
          setTimeout(function() {
            //碰到set异步,丢入宏任务队列【set1】:我将它命名为set1
            console.log("2"); //第二轮宏任务执行,输出【2】
            process.nextTick(function() {
              //第二轮宏任务执行,碰到process,丢入微任务队列,【3】
              console.log("3");
            });
            new Promise(function(resolve) {
              //第二轮宏任务执行,输出【2,4】
              console.log("4");
              resolve();
            }).then(function() {
              console.log("5"); //第二轮宏任务执行,碰到then丢入微任务队列,【3,5】
            });
          });
          process.nextTick(function() {
            //碰到process,丢入微任务队列【6】
            console.log("6"); //第一轮微任务执行
          });
          new Promise(function(resolve) {
            console.log("7"); //new的同时执行代码,第一轮主线程此时输出【1,7】
            resolve();
          }).then(function() {
            console.log("8"); //第一轮主线程中promise的then丢入微任务队列,此时微任务队列为【6,8】。当第一轮微任务执行,顺序输出【6,8】
          });
    
          setTimeout(function() {
            //碰到set异步丢入宏任务队列,此时宏任务队列【set1.set2】:我将它命名为set2
            console.log("9"); //第三轮宏任务执行,输出【9】
            process.nextTick(function() {
              //第三轮宏中执行过程中添加到微任务【10】
              console.log("10");
            });
            new Promise(function(resolve) {
              console.log("11"); //第三轮宏任务执行,宏任务累计输出【9,11】
              resolve();
            }).then(function() {
              console.log("12"); //第三轮宏中执行过程中添加到微任务【10,12】
            });
          });
    结果:1,7,8,2,4,5,6,9,11,12,3,10

    例子3:例子2稍微修改

          console.log("1");
    
          setTimeout(function() {
            console.log("2");
            process.nextTick(function() {
              console.log("3");
            });
            new Promise(function(resolve) {
              console.log("4");
            }).then(function() {
              console.log("5");
            });
          });
    
          process.nextTick(function() {
            console.log("6");
          });
    
          new Promise(function(resolve) {
            console.log("7");
            resolve();
          }).then(function() {
            console.log("8");
          });
    
          setTimeout(function() {
            console.log("9");
            process.nextTick(function() {
              console.log("10");
            });
            new Promise(function(resolve) {
              console.log("11");
            }).then(function() {
              console.log("12");
            });
          });
    结果 :1,7,8,2,4,6,9,11,3,10

    关于 async awite 推荐别人写的
    https://blog.csdn.net/VhWfR2u02Q/article/details/84948640

    同步场景

    如:alert()  prompt()

     

    **前端使用异步场景

    在可能发生等待的情况

      1).定时任务:setTimeout setInterval

      2).网络请求:ajax请求,动态<img>加载

      3).事件绑定:addEventListener

    例子1:

    1     console.log(100);
    2     setTimeout(function(){
    3       console.log(200);
    4     },1000);
    5     console.log(300);

      结果:100 300 200

    例子2:

    1     console.log('img开始');
    2     var img = document.createElement('img');
    3     img.onload = function(){
    4       console.log('loaded');
    5     }
    6     img.src="/xxx.png";
    7     console.log('img结束');

         结果:img开始  img结束  loaded

    例子3:

    1     console.log('事件开始!');
    2     var btn1 = document.getElementById('btn1');
    3     btn1.addEventListener('click',function(){
    4       console.log('你点击我了');//点击了才会显示
    5     })
    6     console.log('事件结束!!');

      结果:事件开始!  事件结束! 你点击我了

    例子4:

    1     console.log(1);
    2     setTimeout(function(){
    3       console.log(2);
    4     },1500);
    5     console.log(3);
    6     setTimeout(function(){
    7       console.log(4);
    8     },300);
    9     console.log(5);

      结果:   1 3 5 4 2   //根据封禁时间解封

     

  • 相关阅读:
    yocto/bitbake 学习资源
    QEMU/KVM学习资源
    ubuntu 中创建和删除用户
    git 重命名本地和远程分支
    Ubuntu 上搭建 FTP 服务器
    gdb 常见用法
    git log 显示与特定文件相关的 commit 信息
    基于 qemu system mode 运行 arm 程序
    基于 qemu user mode 运行 aarch64 程序
    checking in(airport)
  • 原文地址:https://www.cnblogs.com/lingXie/p/11493925.html
Copyright © 2011-2022 走看看