Promise异步(一)JS执行机制以及宏任务、微任务

Promise异步

Js执行机制

javaScript语言的一大特性就是单线程,也就是说,同一时间只能做一件事。单线程就意味着,所有任务需要排队,前一个任务结束,才会执行后一个任务,这样导致的问题就是:如果js执行的时间过长,这样就会造成页面的渲染不连贯,导致页面渲染加载阻塞的感觉。

下面来看例子:

<script>
	console.log(1)
	setTimeout(function(){
  		console.log(3)
	},2000)
	console.log(2)
</script>

通过执行以上例子可以看到js会先输出1和2 然后过了两秒之后会打印出来3

在这里插入图片描述

为了解决这个问题,利用多核cpu的计算能力,HTML5提出Web Worker标准,允许javascript脚本创建多个线程,于是js中出现了同步和异步

同步:前一个任务结束之后在执行后一个任务,程序的执行顺序于任务排列顺序是一致的。

异步:异步的概念就是,当我们在做一件事情的时候,如果这件事情就会花费很长的时间,在做这件事情的同时,我们呢还可以去处理其他的事情。

同步任务

同步任务都是在主线程上执行的

异步任务

JS的异步是通过回调函数实现的。

一般的异步任务包括以下三类:

  1. 普通事件,如click、resize等
  2. 资源加载,如load、error等
  3. 定时器,如setInterval、setTimeout等

异步任务的相关回调函数添加到任务队列中,(也称消息队列)

了解了上面就来看看js的执行机制

  1. 首先会执行执行栈中的同步任务
  2. 异步任务(回调函数)放入任务队列中
  3. 一旦执行栈中的所有同步任务执行完成,系统就会按次序读取任务队列的中的异步任务,于是被读取的异步任务结束等待状态,进入执行栈,开始执行。

看一段代码

<script>
	console.log(1)
	document.onclick = function() {
  	console.log('click')
	}
	console.log(2)
	setTimeout(function() {
  	console.log(3)
	},3000)
</script>

开始分析上述代码

  1. 首先将console.log(1) 、document.onclick = fn、console.log(2)、setTimeout(fn,3000)这四个任务放到主线程执行栈当中
  2. 然后执行栈中的同步任务:console.log(1)
  3. 然后到了document.onclick = fn 但是只有当用户点击了之后异步进程处理才会将该任务放到任务队列中去,如果不点击,继续执行下一个任务,console.log(2)
  4. 然后就是到了3秒之后,异步进程处理会将setTimeout(fn,3000)这个任务放到任务队列中
  5. 这时所有的同步任务已经执行完毕, 就会到任务队列中去查有没有异步任务,此时任务队列中有console.log(3)所以将其拿到执行栈中打印3
  6. 此时任务队列已经为空

在这里插入图片描述

如果此时点击了鼠标 ,异步处理进程就会把我们的回调函数放到消息队列中去,尽管同步任务已经结束,还是会到任务队列中查看有没有新的异步任务进来,如果有,将其拿到执行栈中进行打印click

在这里插入图片描述

这里说明,如果在三秒之前点击的话,就先输出click,再输出3

由于主线程不断的重复获得任务,执行任务,在获取任务,在执行,所以这种机制被称为事件循环(event loop)
了解了js的执行机制后,我们在深入的了解一下更精细的任务定义:宏任务,微任务

先上一段代码

<script>
	setTimeout(() => {
  		console.log('定时器')
	}, 0)
	// 这里先记住Promise的then回调都是属于微任务的
	Promise.resolve().then(value => {
  		console.log('Promise')
	})
	console.log('1')
</script>

分析:

现在我们将任务队列分为宏任务队列微任务队列

优先级是 微任务队列 > 宏任务队列

这里的Promise是属于微任务的 所以他的优先级是高于宏任务的

  1. 首先执行同步代码 console.log(‘1’)
  2. 而 setTimeout(fn, 0) 会放到宏任务队列中去,Promise会放到微任务队列中,由于微任务的优先级高于宏任务
  3. 所以会先打印"Promise" 微任务中没有任务后,再去宏任务当中找
  4. 所以最后打印"“定时器”

在这里插入图片描述

在看一段代码

<script>
	setTimeout(() => {
  		console.log('定时器')
	}, 2000)
	console.log('1')
	for(let i = 0; i < 10000; i++){
		console.log("")  
	}
</script>

分析上面代码

  1. 可能有的人会误以为主线程在进行10000次循环后再去执行宏任务时要间隔两秒再输出“定时器”
  2. 但其实并不是这样, 当系统要去解析setTimeout时候,这段代码已经放到了定时器模块,也就是已经开始计时了
  3. 等到时间的时候,他会把这个任务放到宏任务队列里面
  4. 当主线程同步任务执行完毕,会直接拿来宏任务中的setTime模块执行
  5. 所以在输出10000后,立刻就会输出“定时器”,并不会延时两秒

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-wDjYhrfx-1616687465053)(/Users/yinlu/Documents/截屏/截屏2021-03-25 下午11.09.01.png)]

是不是有一点点绕,没关系,我们在看一段代码

<script>
  setTimeout(() => {
  		console.log('定时器1')
	}, 3000)
	setTimeout(() => {
  		console.log('定时器2')
	}, 2000)
	console.log('1')
	for(let i = 0; i < 10000; i++){
		console.log("")  
	}
</script>
  1. 以上代码就意味着,定时器2会优先从定时器模块中放到宏任务队列中去,然后定时器1这个这个任务在放倒定时器2的下面。
  2. 所以当主线程执行完毕后,就会先输出定时器2,再输出定时器1

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-BYBuTPDZ-1616687465054)(/Users/yinlu/Documents/截屏/截屏2021-03-25 下午11.19.21.png)]

现在我们了解了定时器的任务编排之后,我们再来看微任务的处理逻辑

直接上代码

<script>
  setTimeout(() => {
  	console.log('定时器')
	}, 0)
	new Promise(resolve => {
      console.log("Promise")
      resolve();
    }).then(() => {
      console.log('then')
  	})
  console.log("2")
</script>
  1. 首先Promise的构造函数是立刻执行的,他是同步代码
  2. 所以会首先输出"Promise",然后输出2
  3. 然后在将定时器任务拿来放倒宏任务队列中去
  4. 而then方法的回调回放到微任务队列中去
  5. 由于微任务队列优先级 大于 宏任务队列的优先级
  6. 所以先处理微任务队列中的then方法回调,再处理定时器函数
  7. 所以输出应该是,“Promise”、“2”、“then”、“定时器”

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-5nW2TG8o-1616687465056)(/Users/yinlu/Documents/截屏/截屏2021-03-25 下午11.29.16.png)]

那么再复杂一下,我可以将Promise放到定时器中

<script>
  setTimeout(() => {
  	console.log('定时器')
  	new Promise(resolve => {
    	console.log("setTimeout Promise")
    	resolve();
  	}).then(() => {
    	console.log('setTimeout then')
  	})
	}, 0)
	new Promise(resolve => {
    console.log("Promise")
    resolve();
  }).then(() => {
    console.log('then')
  })
	console.log("2")
</script>

好我们来分一下这个

  1. 首先我们找到同步任务Promise的构造函数,以及“2”,所以首先输出“Promise”和“2”,这是肯定的
  2. 然后有一个定时器宏任务和Promise微任务,定时器内的代码先不去看,因为那是在定时器宏任务执行后才执行的
  3. 所以我们会将定时器这个大的宏任务放到宏任务队列中去
  4. 当走到Promise内改变状态了就会产生一个微任务,就是then方法的回调,放到微任务队列中去
  5. 由于微任务队列的优先级高于宏任务,所以先将微任务的then的回调放到主线程中运行,输出“then”
  6. 微任务执行完毕后执行定时器 宏任务队列输出“定时器”
  7. 此时宏任务队列中又产生了同步代码,所以又输出了“setTimeout Promise”,然后又会产生一个微任务就是then方法的回调
  8. 所以最后就会输出“setTimeout then”

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-tsFTr3tw-1616687465057)(/Users/yinlu/Documents/截屏/截屏2021-03-25 下午11.45.01.png)]

现在就对js的运行机制以及宏任务微任务了解透彻了吧!下一篇将开始介绍Promise以及手写Promise!

在这里插入图片描述

  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Y shǔ shǔ

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值