您现在的位置是:主页 > news > 销售型网站模板/东莞网站建设方案外包
销售型网站模板/东莞网站建设方案外包
admin2025/4/28 18:04:16【news】
简介销售型网站模板,东莞网站建设方案外包,开发商城网站,桂林seo公司Promise异步 Js执行机制 javaScript语言的一大特性就是单线程,也就是说,同一时间只能做一件事。单线程就意味着,所有任务需要排队,前一个任务结束,才会执行后一个任务,这样导致的问题就是:如果…
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的异步是通过回调函数实现的。
一般的异步任务包括以下三类:
- 普通事件,如click、resize等
- 资源加载,如load、error等
- 定时器,如setInterval、setTimeout等
异步任务的相关回调函数添加到任务队列中,(也称消息队列)
了解了上面就来看看js的执行机制
- 首先会执行执行栈中的同步任务
- 异步任务(回调函数)放入任务队列中
- 一旦执行栈中的所有同步任务执行完成,系统就会按次序读取任务队列的中的异步任务,于是被读取的异步任务结束等待状态,进入执行栈,开始执行。
看一段代码
<script>console.log(1)document.onclick = function() {console.log('click')}console.log(2)setTimeout(function() {console.log(3)},3000)
</script>
开始分析上述代码
- 首先将console.log(1) 、document.onclick = fn、console.log(2)、setTimeout(fn,3000)这四个任务放到主线程执行栈当中
- 然后执行栈中的同步任务:console.log(1)
- 然后到了document.onclick = fn 但是只有当用户点击了之后异步进程处理才会将该任务放到任务队列中去,如果不点击,继续执行下一个任务,console.log(2)
- 然后就是到了3秒之后,异步进程处理会将setTimeout(fn,3000)这个任务放到任务队列中
- 这时所有的同步任务已经执行完毕, 就会到任务队列中去查有没有异步任务,此时任务队列中有console.log(3)所以将其拿到执行栈中打印3
- 此时任务队列已经为空
如果此时点击了鼠标 ,异步处理进程就会把我们的回调函数放到消息队列中去,尽管同步任务已经结束,还是会到任务队列中查看有没有新的异步任务进来,如果有,将其拿到执行栈中进行打印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是属于微任务的 所以他的优先级是高于宏任务的
- 首先执行同步代码 console.log(‘1’)
- 而 setTimeout(fn, 0) 会放到宏任务队列中去,Promise会放到微任务队列中,由于微任务的优先级高于宏任务
- 所以会先打印"Promise" 微任务中没有任务后,再去宏任务当中找
- 所以最后打印"“定时器”
在看一段代码
<script>setTimeout(() => {console.log('定时器')}, 2000)console.log('1')for(let i = 0; i < 10000; i++){console.log("") }
</script>
分析上面代码
- 可能有的人会误以为主线程在进行10000次循环后再去执行宏任务时要间隔两秒再输出“定时器”
- 但其实并不是这样, 当系统要去解析setTimeout时候,这段代码已经放到了定时器模块,也就是已经开始计时了
- 等到时间的时候,他会把这个任务放到宏任务队列里面
- 当主线程同步任务执行完毕,会直接拿来宏任务中的setTime模块执行
- 所以在输出10000后,立刻就会输出“定时器”,并不会延时两秒
是不是有一点点绕,没关系,我们在看一段代码
<script>setTimeout(() => {console.log('定时器1')}, 3000)setTimeout(() => {console.log('定时器2')}, 2000)console.log('1')for(let i = 0; i < 10000; i++){console.log("") }
</script>
- 以上代码就意味着,定时器2会优先从定时器模块中放到宏任务队列中去,然后定时器1这个这个任务在放倒定时器2的下面。
- 所以当主线程执行完毕后,就会先输出定时器2,再输出定时器1
现在我们了解了定时器的任务编排之后,我们再来看微任务的处理逻辑
直接上代码
<script>setTimeout(() => {console.log('定时器')}, 0)new Promise(resolve => {console.log("Promise")resolve();}).then(() => {console.log('then')})console.log("2")
</script>
- 首先Promise的构造函数是立刻执行的,他是同步代码
- 所以会首先输出"Promise",然后输出2
- 然后在将定时器任务拿来放倒宏任务队列中去
- 而then方法的回调回放到微任务队列中去
- 由于微任务队列优先级 大于 宏任务队列的优先级
- 所以先处理微任务队列中的then方法回调,再处理定时器函数
- 所以输出应该是,“Promise”、“2”、“then”、“定时器”
那么再复杂一下,我可以将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>
好我们来分一下这个
- 首先我们找到同步任务Promise的构造函数,以及“2”,所以首先输出“Promise”和“2”,这是肯定的
- 然后有一个定时器宏任务和Promise微任务,定时器内的代码先不去看,因为那是在定时器宏任务执行后才执行的
- 所以我们会将定时器这个大的宏任务放到宏任务队列中去
- 当走到Promise内改变状态了就会产生一个微任务,就是then方法的回调,放到微任务队列中去
- 由于微任务队列的优先级高于宏任务,所以先将微任务的then的回调放到主线程中运行,输出“then”
- 微任务执行完毕后执行定时器 宏任务队列输出“定时器”
- 此时宏任务队列中又产生了同步代码,所以又输出了“setTimeout Promise”,然后又会产生一个微任务就是then方法的回调
- 所以最后就会输出“setTimeout then”