JavaScript宏任务和微任务 ---事件循环

目录

一、定义

1、JavaScript单线程

2、同步任务(synchronous)

3、异步任务(asynchronous)

二、任务队列(task queue)

三、代码举例说明

四、扩展一下setTimeout

       1 、同步代码执行完了 setTimeout会从0计时吗

    2、两个定时器 上面的定时器先执行 在执行下面的定时器吗?

3、定义一个变量为0   设置两个一样的定时器事件 他会输出什么结果 ?

五、面试题练习

1、第一个 promise+setTimeout

2、第二个promise+setTimeout

3、aync/awit

4、promise的时候

5、微任务队列中创建微任务

6、微任务队列中创建宏任务


一、定义

1、JavaScript单线程

       JavaScript是单线程指的是同一时间只能干一件事情,只有前面的事情执行完,才能执行后面的事情。导致遇到耗时的任务时后面的代码无法执行。

2、同步任务(synchronous)

    console.log('1-0');
    console.log('2-0');
    for (let i = 1; i <= 5; i++) {
      console.log(i);
    }

得到的一定是顺序执行:

JavaScript宏任务和微任务 ---事件循环_第1张图片

3、异步任务(asynchronous

    setTimeout(() => {
      console.log('定时器');
    }, 0)
    console.log('长歌行');

按普通的执行顺序来说 定时器在上面  应该先输出定时器 在输出 长歌行

但实际结果: 长歌行  定时器

原因是 setTimeout是异步任务 

1.宏任务
    页面中的大部分任务都是在主线程上执行的,这些任务包括了:

  1. 渲染事件(如解析 DOM、计算布局、绘制);
  2. 用户交互事件(如鼠标点击、滚动页面、放大缩小等);
  3. JavaScript 脚本执行事件;
  4. 网络请求完成、文件读写完成事件。

为了协调这些任务有条不紊地在主线程上执行,页面进程引入了消息队列和事件循环机制,渲染进程内部会维护多个消息队列,比如延迟执行队列和普通的消息队列。然后主线程采用一个 for 循环,不断地从这些任务队列中取出任务并执行任务。我们把这些消息队列中的任务称为宏任务。

2.微任务
       基于微任务的技术有 MutationObserver、Promise 以及以 Promise 为基础开发出来的很多其他的技术。所以微任务的重要性也与日俱增,了解其底层的工作原理对于你读懂别人的代码,以及写出更高效、更具现代的代码有着决定性的作用。

二、任务队列(task queue)

通过上面代码知道setTimeout是异步的   我们就搞清了执行顺序优先级  同步代码>异步代码      所以说 在任务队列中 分为两大类 1.同步任务   2. 异步任务 

1.执行栈

(1)所有同步任务都在主线程上执行,形成一个执行栈(execution context stack)。

(2)主线程之外,还存在一个"任务队列"(task queue)。只要异步任务有了运行结果,就在"任务队列"之中放置一个事件。

(3)一旦"执行栈"中的所有同步任务执行完毕,系统就会读取"任务队列",看看里面有哪些事件。那些对应的异步任务,于是结束等待状态,进入执行栈,开始执行。

(4)主线程不断重复上面的第三步,称为事件循环(Event Loop)。

宏任务中的方法:1. script (可以理解为外层同步代码,作为入口 )   2. setTimeout/setInterval

微任务中的方法:1.Promise 2. nextTick

而他们的执行顺序 是 微任务 先输出 在输出 宏任务

三、代码举例说明

    setTimeout(() => {
      console.log('定时器');
    }, 0)
    new Promise((resolve) => {
      console.log('同步代码')  
      resolve('异步代码')
    }).then((res) => {
      console.log(res);   
    })
    console.log('长歌行');

运行结果:

JavaScript宏任务和微任务 ---事件循环_第2张图片

注意:new Promise是创建一个构造函数 这个过程是同步的,而.then方法是异步的  所以代码先执行 同步>微任务>宏任务

四、扩展一下setTimeout

       1 、同步代码执行完了 setTimeout会从0计时吗

    setTimeout(() => {
      console.log('setTimeout');
    }, 1000);
    console.log('长歌行');
    for (let i = 0; i < 1000; i++) {
      console.log('');
    }

运行结果:

JavaScript宏任务和微任务 ---事件循环_第3张图片

    2、两个定时器 上面的定时器先执行 在执行下面的定时器吗?

    setTimeout(() => {
      console.log('setTimeout1');
    }, 2000);
    setTimeout(() => {
      console.log('setTimeout2');
    }, 1000);

运行结果:

3、定义一个变量为0   设置两个一样的定时器事件 他会输出什么结果 ?

    i = 0
    setTimeout(() => {
      console.log(++i); 
    }, 1000);
    setTimeout(() => {
      console.log(++i);  
    }, 1000);

运行结果: 1  2

五、面试题练习

1、第一个 promise+setTimeout

console.log('a1'); 
 
Promise.resolve().then(() => {
    console.log('Promise-1'); 
 
    setTimeout(() => {
        console.log('setTimeout-2');
    }, 0);
});
 
setTimeout(() => {
    console.log('setTimeout-1');
 
    Promise.resolve().then(() => {
        console.log('Promise-2');
    });
}, 0);
 
console.log('a2');  
 

        运行结果是:

a1
a2
Promise-1
setTimeout-1
Promise-2
setTimeout-2
 

运行解释:

第一轮
1.0  a1 第一轮 执行的全局任务。
1.1  Promise.resolve() 加入到第一轮的微任务队列中。
1.2  setTimeout('setTimeout-1') 生成宏任务队列一。
1.3  a2 第一轮 最后一个执行的全局任务,并检查第一轮微任务队列。
1.4  'Promise-1' 微任务被执行。
1.5  setTimeout('setTimeout-2') 生成宏任务队列二。
// 第一轮事件循环结束, 依次输出 a1, a2, Promise-1

// 第二轮
 2.0 'setTimeout-1' 执行在第一轮生成宏任务队列1, 宏任务队列清空,检查微任务队列。
 2.1 'Promise-2' 执行第二轮加入的微任务。
// 第二轮结束,依次输出 setTimeout-1,Promise-2
 
// 第三轮
3.0 'setTimeout-2' 执行第一轮生成的宏任务队列二,输出'setTimeout-2' 结束。

2、第二个promise+setTimeout

    console.log('1');
 
    setTimeout(function () {
      console.log('2');
      process.nextTick(function () {
        console.log('3');
      })
      new Promise(function (resolve) {
        console.log('4');
        resolve();
      }).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');
        resolve();
      }).then(function () {
        console.log('12')
      })
    })

运行结果:

 1、7 、6、8、2、4、3、5、9、11、10、12

运行解释:

第一轮 执行外面同步代码 : 1     7   

第二轮 执行 微任务 : 6    8    

第三轮 宏任务  第一个setTimeout : 同步  2  4   微任务 3   5   第二个setTimeout:同步  9   11    微任务  10   12

3、aync/awit

			console.log('script start')
			async function async1() {
				await async2()
				console.log('async1 end')
			}
			async function async2() {
				console.log('async2 end')
			}
			async1()
			setTimeout(function() {
				console.log('setTimeout')
			}, 0)
			new Promise(resolve => {
					console.log('Promise')
					resolve()
				})
				.then(function() {
					console.log('promise1')
				})
				.then(function() {
					console.log('promise2')
				})
			console.log('script end')

运行结果:

JavaScript宏任务和微任务 ---事件循环_第4张图片

新版本---运行详解:

1、执行console.log('script start')
2、执行async1()
     1、执行async2()
          console.log('async2 end')
     2、await async2()后边的内容放入微任务队列,让出主线程
3、setTimeout放入宏任务队列
4、执行Promise
     1、console.log('Promise')
      2、第一个then放入微任务队列
5、console.log('script end') 主线程执行完毕
6、执行微任务队列
      1、执行console.log('async1 end')
      2、执行第一个then console.log('promise1'),放入第二个then
      3、执行第二个then console.log('promise2')
7、执行宏任务队列 console.log('setTimeout')

4、promise的时候

console.log('script start');
new Promise((resolve) => {
	console.log('promise 1');
	resolve();
}).then(() => {
	console.log('promise 2');
})
console.log('script end');


运行结果:
   script start
   promise 1
   script end
   promise 2

promise声明了就会直接调用的哦。但是执行到promise的then方法就会将其放入微任务队列先不执行,继续执行后边的哦

5、微任务队列中创建微任务

JavaScript宏任务和微任务 ---事件循环_第5张图片

运行详解:

1、promise在主线程上,执行promise,输出promise1

      1、遇到setTimeout放入宏任务队列中

      2、遇到promise1的then方法会放入微任务队列

2、执行微任务队列,里边有个promise

         promise2的then方法放入微任务队列(也就是promise1的后边)

3、执行宏任务队列

6、微任务队列中创建宏任务

console.log('script start');
new Promise((resolve) => {
	console.log('Promise');
    resolve();
}).then(() => {
	console.log('Promise.then');
    setTimeout(() => {
	    console.log('setTimeout1');
    }, 1000)
})
setTimeout(() => {
	console.log('setTimeout2');
}, 100)
console.log('script end');

运行结果:

JavaScript宏任务和微任务 ---事件循环_第6张图片

图解:

1、promise在主线程上,执行promise,输出promise1
     遇到promise的then方法会放入微任务队列
2、执行微任务队列,里边有个setTimeout
     setTimeout放入微任务队列的宏任务队列中(也就是新建一个宏任务队列)
3、执行宏任务队列,100在前,1000在后,先输出setTimeout2,后输出setTimeout1


执行完文任务队列,会从宏任务队列中选出优先级较高的执行

JavaScript宏任务和微任务 ---事件循环_第7张图片

 

你可能感兴趣的:(JavaScript,javascript,前端)