浅谈js中的宏任务和微任务

目录
  • 1、关于JavaScript
  • 2、JavaScript事件循环
  • 3、宏任务和微任务
  • 4、拓展宏任务微任务

下面一道关于宏任务和微任务的题:

setTimeout(function(){
  console.log('1')
});

new Promise(function(resolve){
  console.log('2');
     resolve();
}).then(function(){
 console.log('3')
});

console.log('4')

试问一下上面代码的执行顺序是啥?

有小伙伴可能会答:2,4,1,3

估摸着是这么想的:我难道不知道js是一行一行执行的,setTimeout是异步,所以先放后面;往下走,执行了console.log(2) ,.then()是异步的,放在后面;走了console.log(4);再去异步队列里走,先是console.log(1);再是console.log(3)

我有点慌,于是我粘贴到浏览器去瞅两眼:

无法相信

带着困惑的我,只能去好好研究研究JavaScript的运行机制了!

1、关于JavaScript

JavaScript是一门单线程语言,即一次只能完成一个任务,若有多个任务要执行,则必须排队按照队列来执行(前一个任务完成,再执行下一个任务)。

2、JavaScript事件循环

既然js是单线程,那就像只有一个窗口的食堂,学生需要排队一个一个打饭,同理js任务也要一个一个顺序执行。这种模式执行简单,但随着日后的需求,事务,请求增多,这种单线程模式执行效率必定低下。只要有一个任务执行消耗了很长时间,在这个时间里后面的任务无法执行。

常见的有新闻包含的超清图片加载很慢,难道我们的网页要一直卡着直到图片完全显示出来?为了解决这个问题,JavaScript语言将任务执行模式分成同步和异步:

  • 同步模式: 就是上面所说的一种执行模式,后一个任务等待前一个任务结束,然后再执行,程序的执行顺序与任务的排列顺序是一致的、同步的。
  • 异步模式: 就是每一个任务有一个或多个回调函数(callback),前一个任务结束后,不是执行后一个任务,而是执行回调函数,后一个任务则是不等前一个任务结束就执行,所以程序的执行顺序与任务的排列顺序是不一致的、异步的。

导图要表达的内容用文字来表述的话:

  • 同步和异步任务分别进入不同的执行"场所",同步的进入主线程,异步的进入Event Table并注册函数。
  • 当指定的事情完成时,Event Table会将这个函数移入Event Queue
  • 主线程内的任务执行完毕为空,会去Event Queue读取对应的函数,进入主线程执行。
  • 上述过程会不断重复,也就是常说的Event Loop(事件循环)。

 再配上代码表达:

let data = [];
$.ajax({
    url:blog.csdn.net,
    data:data,
    success:() => {
        console.log('发送成功!');
    }
})
console.log('代码执行结束');

上面是一段简易的ajax请求代码:

  • ajax进入Event Table,注册回调函数success
  • 执行console.log('代码执行结束')。
  • ajax事件完成,回调函数success进入Event Queue
  • 主线程从Event Queue读取回调函数success并执行。

相信通过上面的文字和代码,你已经对js的执行顺序有了初步了解。然而这也是为什么会有小伙伴回答2,4,1,3的原因。

然而实际上,异步队列里是还有门道的,我们那道面试题,setTimeoutpromise.then()都在异步队列了!接下来,讲讲那些门道(宏任务和微任务)。

3、宏任务和微任务

每个人的理解方式不同,因为宏任务和微任务并不是标准,但执行的顺序在js中是大一统了的。

宏任务:

# 浏览器 Node
<script>整体代码
setTimeout
setInterval
setImmediate x
requestAnimationFrame x
Ajax
DOM事件

微任务:

# 浏览器 Node
process.nextTick x
MutationObserver x
Promise.then catch finally

宏任务包括:<script>整体代码、setTimeoutsetIntervalsetImmediateAjax、DOM事件
微任务process.nextTickMutationObserverPromise.then catch finally

process.nextTick差异太大,不同的node执行不统一,不做标准
微任务比宏任务的执行时间要早

Tip:有些人喜欢将<script>整体代码放在宏任务里,但我个人不喜欢,在我这里它只是第一执行的主线程,我个人是将宏任务和微任务都归类到异步任务里!

我们再来看看那道面试题:

//回调才是异步任务
setTimeout(function(){//宏任务
  console.log('1')
});

new Promise(function(resolve){
  console.log('2');//同步主线程
     resolve();
}).then(function(){//微任务
 console.log('3')
});

console.log('4')//同步主线程

2:同步中的第一个,故第一

4:同步中的第二个,故第二

3:异步中的微任务,故第三

1:异步中的宏任务,故第二

因此:2,4,3,1的结果就出来了!

除此我们来拓展一下:

setTimeout(() => {//宏任务队列1
  console.log('1');//宏任务队1的任务1

  setTimeout(() => {//宏任务队列3(宏任务队列1中的宏任务)
    console.log('2')
  }, 0)

  new Promise(resolve => {
    resolve()
    console.log('3')//宏任务队列1的任务2
  }).then(() => {//宏任务队列1中的微任务
    console.log('4')
  })

}, 0)

setTimeout(() => {//宏任务队列2
  console.log('5')
}, 0)

console.log('6')//同步主线程

执行整体代码(宏任务)console.log('6') >> 宏任务队列1、宏任务队列2位异步(依次执行)

宏任务队列1:=>

console.log('1')

console.log('3')

console.log('4')//宏任务中的微任务

剩下的不会先执行,因为是宏任务中的宏任务(console.log(2)) ,要被继续丢进任务队列后

宏任务队列2:=>

console.log('5')

宏任务队列1中的宏任务3:=>

console.log('2')

以上代码会怎么输出呢?

4、拓展宏任务微任务

上面出了复杂的题,小伙伴们不妨可以想一想,这种复杂情况,一个套一个的该怎么执行呢?

  • 6:第一个同步主线程,故第一

script整体代码里没有微任务故直接执行宏任务=>

宏任务队列:

宏任务队列1

  • 任务1:console.log(1)
  • 任务2:console.log(3)
  • 宏任务队列1中的微任务:console.log(4)
  • 宏任务队列3:因他是宏任务队列1中的宏任务,所以被丢进了任务队列最后,我们先看宏任务队列1同级的是否还有宏任务,有就先执行同级的,没有才可以执行宏任务队列3!故最后!

宏任务队列2  

console.log(5)

所以输出的结果是什么?是6,1,3,4,5,2!

经过验证,结果正确!

到此这篇关于浅谈js中的宏任务和微任务的文章就介绍到这了,更多相关js中的宏任务和微任务内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

(0)

相关推荐

  • JS事件循环机制event loop宏任务微任务原理解析

    首先看一段代码 async function (){ await f2() console.log('f1') } async function f2(){ console.log('f2') } console.log('正常1') f1() setTimeout(()=>{ console.log('定时器') }) console.log('正常2') 正确的打印顺序应该是:正常1,f2 ,正常2,f1,定时器 为什么会出现这样打印顺序呢 首先javascript是一门单线程语言,在最新的

  • 浅谈javascript事件环微任务和宏任务队列原理

    JS 事件环 JS 程序的运行是离不开事件环机制的,这个机制保证在发生某些事情的时候我们有机会执行一个我们事先预定好的函数,事情发生的时候 JS 会将相应的函数入栈执行然后出栈,但是关于事件环我们还有一些未知的东西,例如,setTimeout 我们习惯称他为定时器,但是可能很多人没有意识到,这个东西和我们常用的一些事件没什么不同,只不过我们通常所说的事件大多需要用户触发,而 setTimeout 不用用户自己触发,而是指定时间之后触发:那么问题来了,如果我们将时间设置为 0 会发生什么?会立即执

  • 浅谈JavaScript宏任务和微任务执行顺序

    目录 一.JavaScript单线程 1. 同步任务(synchronous) 2. 异步任务(asynchronous) 二.任务队列(task queue) 1.执行栈 扩展一下setTimeout的理解 一.JavaScript单线程 JavaScript是单线程指的是同一时间只能干一件事情,只有前面的事情执行完,才能执行后面的事情.导致遇到耗时的任务时后面的代码无法执行. 在此之前啊 我们必须了解同步和异步 1. 同步任务(synchronous) console.log(123); c

  • JavaScript 操作宏任务与微任务

    宏任务与微任务 javaScript是单线程语言(如果多线程dom会疯掉) 所以在同一时间只能执行一个任务,称为主线程,用来执行同步任务 同时还有两个任务列表用于存放异步任务,宏任务.微任务 执行顺序为:主线程=>微任务=>宏任务 有关定时器 定时器模块,到达时间点将其放入宏任务队列 如果主线程没有任务则执行,如果有则等待执行完成后再继续执行 如果有两个相同时间的定时器则上面的先执行下面的后执行 如果两个时间不同的定时器则时间短的先执行时间长的后执行 注意点: 定时器的⏲是在定时器模块中完成的

  • JavaScript事件循环及宏任务微任务原理解析

    首先看一段代码: 打印顺序是什么? 正确答案:script start, script end, promise1, promise2, setTimeout 其中涉及到事件循环(event loop),宏任务(macrotask),微任务(microtask) 一.事件循环 Event Loop 程序中设置两个线程:一个负责程序本身的运行,称为"主线程":另一个负责主线程与其他进程(主要是各种I/O操作)的通信,被称为"Event Loop线程"(可以译为&quo

  • 浅谈js中的宏任务和微任务

    目录 1.关于JavaScript 2.JavaScript事件循环 3.宏任务和微任务 4.拓展宏任务微任务 下面一道关于宏任务和微任务的题: setTimeout(function(){ console.log('1') }); new Promise(function(resolve){ console.log('2'); resolve(); }).then(function(){ console.log('3') }); console.log('4') 试问一下上面代码的执行顺序是啥

  • 浅谈js中的in-for循环

    浅谈js中的in-for循环 <!doctype html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Document</title> <script type="text/javascript" src="jquery.js"></script> </hea

  • 浅谈js中的三种继承方式及其优缺点

    第一种,prototype的方式: //父类 function person(){ this.hair = 'black'; this.eye = 'black'; this.skin = 'yellow'; this.view = function(){ return this.hair + ',' + this.eye + ',' + this.skin; } } //子类 function man(){ this.feature = ['beard','strong']; } man.pr

  • 浅谈js中的this问题

    this this的指向在函数定义的时候是确定不了的,只有函数执行的时候才能确定this到底指向谁,实际上 this的最终指向的是那个调用它的对象(这里其实并不完全对,this的指向有时候会很微妙,得靠自己去慢慢体会) 只有方法在对象上,对象调用当前方法,指向当前对象 function fnThis(){ let user='js'; console.log(this.user)//undefined console.log(this)//global(window) } fnThis(); 这

  • 浅谈js中字符和数组一些基本算法题

    最近在刷 fcc的题,跟升级打怪一样,一关一关的过,还挺吸引我的.今天抽时间把 Basic Algorithm Scritping  这部分题做了,根据一些提示,还是比较简单的.有些题的处理方式 方法,我想值得借鉴.比如在项目中有时候要处理一个字符,如果想不到一些相关的方法,还挺费事的,所以,在此记录下来,如果以后遇到一些字符或者数组处理,可以来翻翻这篇文章,希望以此得到一些提示而不是去翻文档. 看到此博文的博友,有更好更简单的代码或者好的想法,请留言交流(我一直觉得只有学习别人的优秀代码才能进

  • 浅谈JS中的三种字符串连接方式及其性能比较

    工作中经常会碰到要把2个或多个字符串连接成一个字符串的问题,在JS中处理这类问题一般有三种方法,这里将它们一一列出顺便也对它们的性能做个具体的比较. 第一种方法 用连接符"+"把要连接的字符串连起来: str="a"; str+="b"; 毫无疑问,这种方法是最便捷快速的,如果只连接100个以下的字符串建议用这种方法最方便. 第二种方法 以数组作为中介用 join 连接字符串: var arr=new Array(); arr.push(a);

  • 浅谈js中function的参数默认值

    func(string1,url,flag,icon),然后在另一个asp中调用它func(a,b),那flag和icon的值是什么,怎么定义默认值?谢谢! --默认值应该是 undefined 在函数内预设数可以用 arguments[i] i就是你参数的位置 第一个为0 所以要设定 flag 的默认值 则可以这么写 function func(string1,url,flag,icon){ if(!arguments[2]) flag = "123"; if(!arguments[

  • 浅谈JS中逗号运算符的用法

    注意: 一.由于目前正在功读JavaScript技术,所以这里拿JavaScript为例.你可以自己在PHP中试试. 二.JavaScript语法比较复杂,因此拿JavaScript做举例. 最近重新阅读JavaScript权威指南这本书,应该说很认真的阅读,于是便想把所学的东西多记录下来.后 面本人将逐步写上更多关于本书的文章. 本文的理论知识来自于JavaScript权威指南,我这里做一下整理,或者说叫笔记. 如果你的基础够好的话,完全理解不成问题,但是如果读得有些郁闷的话,可以加我的QQ:

  • 浅谈js中test()函数在正则中的使用

    test() 方法用于检测一个字符串是否匹配某个模式. 返回一个 Boolean 值,它指出在被查找的字符串中是否匹配给出的正则表达式. regexp.test(str) 参数 regexp 必选项.包含正则表达式模式或可用标志的正则表达式对象. str    必选项.要在其上测试查找的字符串. 说明 test 方法检查字符串是否与给出的正则表达式模式相匹配,如果是则返回 true,否则就返回 false. 每个正则表达式都有一个 lastIndex 属性,用于记录上一次匹配结束的位置. var

  • 浅谈js中的引用和复制(传值和传址)

    好像一般很少人讲到js中的引用和复制,不过弄清楚这个概念可以帮助理解很多东西 先讲一下很基础的东西,看看js中几种数据类型分别传的什么 引用:对象.数组.函数 复制:数字.布尔 字符串单独说明,因为它的特殊性,无法确定是传递引用还是复制数值(因为字符串的值是没法改变的,所以纠结这个问题也是没意义的)但是用于比较的时候显然是属于传值比较(稍后具体说比较的事) 下面讲一下在使用中的具体体现 最普通的使用就是赋值了 var a = 1; var b = a; //赋的是a的复制值 b ++; aler

随机推荐