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   //根据封禁时间解封

 

原文地址:https://www.cnblogs.com/lingXie/p/11493925.html