您的位置:时时app平台注册网站 > web前端 > JavaScript运维机制【彩世界网址】

JavaScript运维机制【彩世界网址】

2019-11-08 03:49
外层宏事件1
外层宏事件2
微事件1
微事件2
内层宏事件3

4.主线程 从event  queue中读取 回调函数success 并执行

渐渐的 setTimeout 用的地方多了,问题也出现了,有时候明明写的延时3秒,实际却5,6秒才执行函数,这又咋回事啊?

彩世界网址 1


彩世界网址 2

• 遇到 Promise, new Promise 直接执行 输出 外层宏事件2


任务进入执行栈——>同步任务还是异步任务?

 

• 执行then 被分发到微任务Event Queue中``


 

•第一轮宏任务执行结束,开始执行微任务 打印 '微事件1' '微事件2'

setInterval

会每隔指定的时间将注册的函数置入 event  queue


setInterval(fn, ms):  不是每过ms 会执行一次fn,而是,没过ms,会有fn进入event  queue,一旦fn执行时间超过了延迟时间ms,那就看不出来有时间间隔了


3秒到了,计时事件 timeout 完成, task() 进入Event Queue,但是 sleep 也太慢了吧,还没执行完,只好等着。

setTimeout(() => {
    //执行后 回调一个宏事件
    console.log('内层宏事件3')
}, 0)
console.log('外层宏事件1');

new Promise((resolve) => {
    console.log('外层宏事件2');
    resolve()
}).then(() => {
    console.log('微事件1');
}).then(()=>{
    console.log('微事件2')
})

因为js是单线程,所有js任务要一个一个顺序执行,任务分为:

第三轮事件循环开始,此时只剩setTimeout2了,执行。

 

              异步任务***

  console.log('马上执行for循环啦');
  for (var i = 0; i < 10000; i ) {
    i == 99 && resolve();
  }
}).then(function() {

• 首先浏览器执行js进入第一个宏任务进入主线程, 遇到 setTimeout  分发到宏任务Event Queue中

上面的过程不断重复——就是常说的Event  Loop(事件循环)**

 

微任务

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

 这个例子看懂基本js执行机制就理解了

//主线程直接执行
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')
    })
})
//微事件1
process.nextTick(function() {
    console.log('6');
})
//主线程直接执行
new Promise(function(resolve) {
    console.log('7');
    resolve();
}).then(function() {
    //微事件2
    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')
    })
})

• 首先浏览器执行js进入第一个宏任务进入主线程, 直接打印console.log('1')

• 遇到 setTimeout  分发到宏任务Event Queue中

• 遇到 process.nextTick 丢到微任务Event Queue中

• 遇到 Promise, new Promise 直接执行 输出 console.log('7');

• 执行then 被分发到微任务Event Queue中``

•第一轮宏任务执行结束,开始执行微任务 打印 6,8

•第一轮微任务执行完毕,执行第二轮宏事件,执行setTimeout

•先执行主线程宏任务,在执行微任务,打印'2,4,3,5'

•在执行第二个setTimeout,同理打印 ‘9,11,10,12’

•整段代码,共进行了三次事件循环,完整的输出为1,7,6,8,2,4,3,5,9,11,10,12。

以上是在浏览器环境下执行的数据,只作为宏任务和微任务的分析,我在node环境下测试打印出来的顺序为:1,7,6,8,2,4,9,11,3,10,5,12。node环境执行结果和浏览器执行结果不一致的原因是:浏览器的Event loop是在HTML5中定义的规范,而node中则由libuv库实现。libuv库流程大体分为6个阶段:timers,I/O callbacks,idle、prepare,poll,check,close callbacks,和浏览器的microtask,macrotask那一套有区别。

参考文档

    

  

//主线程直接执行

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')

})

})

//微事件1

process.nextTick(function() {

console.log('6');

})

//主线程直接执行

new Promise(function(resolve) {

console.log('7');

resolve();

}).then(function() {

//微事件2

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.ajax进入Event Table,注册回调函数 success()

setTimeout(function() {

macrotask 和 microtask 表示异步任务的两种分类。

let data = [];

$.ajax({

url:www.javascript.com,

data:data,

success:() => { console.log('发送成功!'); } })

console.log('代码执行结束')

第二轮事件循环结束,第二轮输出2,4,3,5。

先看个例子

一.js是一门单线程的语言,js是按照语句出现的顺序执行的

遇到 console.log() ,立即执行。

.宏任务(macrotask )和微任务(microtask )

2.执行同步任务 console.log('代码执行结束')

Promise 的定义和功能本文不再赘述,不了解的读者可以学习一下阮一峰老师的Promise。而 process.nextTick(callback) 类似node.js版的"setTimeout",在事件循环的下一次循环中调用 callback 回调函数。

我们看看打印结果


 

•第一轮微任务执行完毕,执行第二轮宏事件,打印setTimeout里面内容'内层宏事件3'


             同步任务

第三轮事件循环结束,第三轮输出9,11,10,12。

在挂起任务时,JS 引擎会将所有任务按照类别分到这两个队列中,首先在 macrotask 的队列(这个队列也被叫做 task queue)中取出第一个任务,执行完毕后取出 microtask 队列中的所有任务顺序执行;之后再取 macrotask 任务,周而复始,直至两个队列的任务都取完。

总结

1.js的异步

js是一门单线程的语言,无论是什么新框架新语法实现的所谓异步,都是用同步的方法去模拟的

2.事件循环event  Loop

事件循环是js实现异步的一种方法,也是js的执行机制

3.js的执行与运行

js在不同的环境下,不如node,浏览器等,执行方式是不同的

运行大多指js解析引擎,是统一的


作者:ssssyoki

链接:

 

宏任务

 

# 浏览器 Node
setTimeout
setInterval
setImmediate x
requestAnimationFrame x

 


事件循环,宏任务,微任务的关系如图所示:

宏任务和微任务之间的关系

Promise 与 process.nextTick(callback)

process.nextTick(callback)类似 node.js版的“setTimeout”,在事件循环的下一次循环中调用callback回调函数

任务更精细的定义:

宏任务: 包括整体代码script, setTimeout, setInterval

微任务:promise, process.nextTick

事件循环循序:进入整体代码(宏任务)后,开始第一次循环,接着执行所有的微任务,然后再次从宏任务开始,找到其中一个任务队列执行完毕,再执行所有的微任务

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

new Promise(function(resolve) { console.log('promise');

}).then(function() { console.log('then'); })

console.log('console')

// promise 

//console

//undefined

//setTimeout

分析:1.这段代码作为宏任务,进入主线程

2.先遇到settimout,那么将其回调函数注册后分发到宏任务event queue

3.接下来promise,立即执行,并将then函数分发到微任务event queue

4.遇到 console.log并执行

5.整体代码script作为第一个宏任务执行结束,看看有哪些微任务?then函数在微任务中,并执行

6.第一轮事件循环结束后,开始第二轮循环,从宏任务event queue开始,发现 了宏任务 setimout对应的回调函数,立即执行

7.结束

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') }) })

分析

第一轮事件循环流程分析如下:

整体script作为第一个宏任务进入主线程,遇到console.log,输出1。

遇到setTimeout,其回调函数被分发到宏任务Event Queue中。我们暂且记为setTimeout1。

遇到process.nextTick(),其回调函数被分发到微任务Event Queue中。我们记为process1。

遇到Promise,new Promise直接执行,输出7。then被分发到微任务Event Queue中。我们记为then1。

又遇到了setTimeout,其回调函数被分发到宏任务Event Queue中,我们记为setTimeout2。

宏任务Event Queue微任务Event Queue

setTimeout1process1

setTimeout2then1

上表是第一轮事件循环宏任务结束时各Event Queue的情况,此时已经输出了1和7。

我们发现了process1和then1两个微任务。

执行process1,输出6。

执行then1,输出8。

好了,第一轮事件循环正式结束,这一轮的结果是输出1,7,6,8。那么第二轮时间循环从setTimeout1宏任务开始:

首先输出2。接下来遇到了process.nextTick(),同样将其分发到微任务Event Queue中,记为process2。new Promise立即执行输出4,then也分发到微任务Event Queue中,记为then2。

宏任务Event Queue微任务Event Queue

setTimeout2process2

then2

第二轮事件循环宏任务结束,我们发现有process2和then2两个微任务可以执行。

输出3。

输出5。

第二轮事件循环结束,第二轮输出2,4,3,5。

第三轮事件循环开始,此时只剩setTimeout2了,执行。

直接输出9。

将process.nextTick()分发到微任务Event Queue中。记为process3。

直接执行new Promise,输出11。

将then分发到微任务Event Queue中,记为then3。

宏任务Event Queue微任务Event Queue

process3

then3

第三轮事件循环宏任务执行结束,执行两个微任务process3和then3。

输出10。

输出12。

第三轮事件循环结束,第三轮输出9,11,10,12。

整段代码,共进行了三次事件循环,完整的输出为1,7,6,8,2,4,3,5,9,11,10,12。

(请注意,node环境下的事件监听依赖libuv与前端环境不完全相同,输出顺序可能会有误差)


输出5。

• 遇到 console.log() 直接执行 输出 外层宏事件1


我们进入正题,除了广义的同步任务和异步任务,我们对任务有更精细的定义:

彩世界网址 3

setTimeout

setTimeout(() => { task() },3000)

sleep(10000000)

分析:1.task()进入event  table并注册,计时开始

2.执行同步任务 sleep()

3.3秒到了,但是同步任务未完成,所以event queue中仍需等待

4.执行完,task()从 event queue进入主线程执行,延迟时间大于3秒

setTimeout(fn,0)

指某个任务在主线程最早可得的空闲时间执行,即主线程执行栈中的同步任务完成后,立即执行fn,0毫秒是达不到的,最低是4毫秒


输出3。

掘金上面盗张图记录一下

异步任务——>event table——>注册回调函数——>event queue——>**读取任务队列中的结果,进入主线程执行**

这段代码作为宏任务,进入主线程。


同步任务
——>主线程——>任务全部执行完毕——>读取任务队列中的结果,进入主线程执行**

表是第一轮事件循环宏任务结束时各Event Queue的情况,此时已经输出了1和7。

二.Javascript事件循环

将 process.nextTick() 分发到微任务Event Queue中。记为 process3 。

3.ajax事件完成,回调函数 进入event queue

  console.log('延时3秒');


流程图:

setTimeout(() => {

 

主线程从Event Queue读取回调函数 success 并执行。

4.又恨又爱的setInterval

micro-tasks queue macro-tasks queue

 

3.又爱又恨的setTimeout

遇到 setTimeout ,其回调函数被分发到宏任务Event Queue中。我们暂且记为 setTimeout1 。

结束。

sleep 终于执行完了, task() 终于从Event Queue进入了主线程执行。

 

我们发现了 process1 和 then1 两个微任务。

去chrome上验证下,结果完全不对,瞬间懵了,说好的一行一行执行的呢?

看到这里读者要打人了:我难道不知道js是一行一行执行的?还用你说?稍安勿躁,正因为js是一行一行执行的,所以我们以为js都是这样的:

(1)js的异步

micro-tasks queue macro-tasks queue

task() 进入Event Table并注册,计时开始。

 

console.log('1');
setTimeout(function() {
console.log('2');
new Promise(function(resolve) {
console.log('4');
resolve();
}).then(function() {
console.log('5')
})
}) new Promise(function(resolve) {
console.log('7');
resolve();
}).then(function() {
console.log('8')
}) setTimeout(function() {
console.log('9');
new Promise(function(resolve) {
console.log('11');
resolve();
}).then(function() {
console.log('12')
})
})输出:1 7 8 2 4 5 9 11 12

我们从最开头就说javascript是一门单线程语言,不管是什么新框架新语法糖实现的所谓异步,其实都是用同步的方法去模拟的,牢牢把握住单线程这点非常重要。

输出10。

代码2的输出结果是:

首先输出2。接下来遇到了 process.nextTick() ,同样将其分发到微任务Event Queue中,记为 process2 。 new Promise 立即执行输出4, then 也分发到微任务Event Queue中,记为 then2 。

  • 同步和异步任务分别进入不同的执行"场所",同步的进入主线程,异步的进入Event Table并注册函数。

  • 当指定的事情完成时,Event Table会将这个函数移入Event Queue。

  • 主线程内的任务执行完毕为空,会去Event Queue读取对应的函数,进入主线程执行。

  • 上述过程会不断重复,也就是常说的Event Loop(事件循环)。

整段代码,共进行了三次事件循环,完整的输出为1,7,6,8,2,4,3,5,9,11,10,12。

接下来遇到了 Promise , new Promise 立即执行, then 函数分发到微任务Event Queue。

不论你是javascript新手还是老鸟,不论是面试求职,还是日常开发工作,我们经常会遇到这样的情况:给定的几行代码,我们需要知道其输出内容和顺序。因为javascript是一门单线程语言,所以我们可以得出结论:

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

2.javascript事件循环

setTimeout(() = >{
  task();
  },
3000) console.log('执行console');

    console.log('执行then函数啦')
});

相信通过上面的文字和代码,你已经对js的执行顺序有了初步了解。接下来我们来研究进阶话题:setTimeout。

  • 同步任务

  • 异步任务

我们不禁要问了,那怎么知道主线程执行栈为空啊?js引擎存在monitoring process进程,会持续不断的检查主线程执行栈是否为空,一旦为空,就会去Event Queue那里检查是否有等待被调用的函数。

macro-task(宏任务) script,setTimeout,setInterval

我们还经常遇到 setTimeout(fn,0) 这样的代码,0秒后执行又是什么意思呢?是不是可以立即执行呢?

我们真的要彻底弄明白javascript的执行机制了。

执行 console.log('代码执行结束') 。

执行 sleep 函数,很慢,非常慢,计时仍在继续。

let a = '1';
console.log(a);
let b = '2';
console.log(b);

将 then 分发到微任务Event Queue中,记为 then3 。

我们来分析一段较复杂的代码,看看你是否真的掌握了js的执行机制:

 

 

直接输出9。

第二轮事件循环宏任务结束,我们发现有 process2 和 then2 两个微任务可以执行。

第一轮事件循环流程分析如下:

micro-tasks queue NodeJS(1 7 6 8     2 4 9 11      3 10 5 12)

//"定时器开始啦"//"马上执行for循环啦"//"执行then函数啦"//"代码执行结束"

1.关于javascript

 

先看一个例子:

事件循环的顺序,决定js代码的执行顺序。进入整体代码(宏任务)后,开始第一次循环。接着执行所有的微任务。然后再次从宏任务开始,找到其中一个任务队列执行完毕,再执行所有的微任务。听起来有点绕,我们用文章最开始的一段代码说明:

执行和运行有很大的区别,javascript在不同的环境下,比如node,浏览器,Ringo等等,执行方式是不同的。而运行大多指javascript解析引擎,是统一的。

 

输出12。

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')
  })
})

乍一看其实差不多嘛,但我们把这段代码在chrome执行一下,却发现控制台执行 task() 需要的时间远远超过3秒,说好的延时三秒,为啥现在需要这么长时间啊?

彩世界网址 4

 

 

修改:

javascript是一门 单线程 语言,在最新的HTML5中提出了Web-Worker,但javascript是单线程这一核心仍未改变。所以一切javascript版的"多线程"都是用单线程模拟出来的,一切javascript多线程都是纸老虎!

//先执行这里// ... 3s later// 执行啦

彩世界网址 5

代码1的输出结果是:

唯一需要注意的一点是,对于 setInterval(fn,ms) 来说,我们已经知道不是每过 ms 秒会执行一次 fn ,而是每过 ms 秒,会有 fn 进入Event Queue。一旦 setInterval 的回调函数 fn 执行时间超过了延迟时间 ms ,那么就完全看不出来有时间间隔了 。这句话请读者仔细品味。

当我们打开网站时,网页的渲染过程就是一大堆同步任务,比如页面骨架和页面元素的渲染。而像加载图片音乐之类占用资源大耗时久的任务,就是异步任务。关于这部分有严格的文字定义,但本文的目的是用最小的学习成本彻底弄懂执行机制,所以我们用导图来说明:

setTimeout(function() {
  console.log('setTimeout');
}) new Promise(function(resolve) {
  console.log('promise');
}).then(function() {
  console.log('then');
}) console.log('console');

 

大名鼎鼎的 setTimeout 无需再多言,大家对他的第一印象就是异步可以延时执行,我们经常这么实现延时3秒执行:

好了,第一轮事件循环正式结束,这一轮的结果是输出1,7,6,8。那么第二轮时间循环从 setTimeout1 宏任务开始:

 

micro-tasks queue macro-tasks queue

关于 setTimeout 要补充的是,即便主线程为空,0毫秒实际上也是达不到的。根据HTML的标准,最低是4毫秒。有兴趣的同学可以自行了解。

这时候我们需要重新理解 setTimeout 的定义。我们先说上述代码是怎么执行的:

setTimeout(() = >{
  task()
  },
3000) sleep(10000000)

说了这么多文字,不如直接一段代码更直白:

ajax进入Event Table,注册回调函数 success 。

//代码1console.log('先执行这里');setTimeout(() => { console.log('执行啦')},0);

//代码2console.log('先执行这里');setTimeout(() => { console.log('执行啦')},3000);

然而实际上js是这样的:

//先执行这里//执行啦

(2)事件循环Event Loop

 

传统的定时器部分我们已经研究过了,接下来我们看下 Promise 与 process.nextTick(callback) 的表现。

  console.log('定时器开始啦')
});
new Promise(function(resolve) {

 

macro-task(宏任务):包括整体代码script,setTimeout,setInterval

 

先遇到 setTimeout ,那么将其回调函数注册后分发到宏任务Event Queue。(注册过程与上同,下文不再描述)

 

既然js是单线程,那就像只有一个窗口的银行,客户需要排队一个一个办理业务,同理js任务也要一个一个顺序执行。如果一个任务耗时过长,那么后一个任务也必须等着。那么问题来了,假如我们想浏览新闻,但是新闻包含的超清图片加载很慢,难道我们的网页要一直卡着直到图片完全显示出来?因此聪明的程序员将任务分为两类:

ok,第一轮事件循环结束了,我们开始第二轮循环,当然要从宏任务Event Queue开始。我们发现了宏任务Event Queue中 setTimeout 对应的回调函数,立即执行。

第三轮事件循环宏任务执行结束,执行两个微任务 process3 和 then3 。

 

上述的流程走完,我们知道setTimeout这个函数,是经过指定时间后,把要执行的任务(本例中为 task() )加入到Event Queue中,又因为是单线程任务要一个一个执行,如果前面的任务需要的时间太久,那么只能等着,导致真正的延迟时间远远大于3秒。

彩世界网址 6

 

执行 process1 ,输出6。

彩世界网址 7

/执行console//task()

根据前面我们的结论, setTimeout 是异步的,应该先执行 console.log 这个同步任务,所以我们的结论是:

执行 then1 ,输出8。

ajax事件完成,回调函数 success 进入Event Queue。

依照 js是按照语句出现的顺序执行 这个理念,我自信的写下输出结果:

 

 

(4)setImmediate

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

  • javascript是按照语句出现的顺序执行的

整体script作为第一个宏任务进入主线程,遇到 console.log ,输出1。

然后我们修改一下前面的代码:

上面说完了 setTimeout ,当然不能错过它的孪生兄弟 setInterval 。他俩差不多,只不过后者是循环的执行。对于执行顺序来说, setInterval 会每隔指定的时间将注册的函数置入Event Queue,如果前面的任务耗时太久,那么同样需要等待。

micro-tasks queue macro-tasks queue

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

 

去验证一下,结果正确!

又遇到了 setTimeout ,其回调函数被分发到宏任务Event Queue中,我们记为 setTimeout2 。

5.Promise与process.nextTick(callback)

不同类型的任务会进入对应的Event Queue,比如 setTimeout 和 setInterval 会进入相同的Event Queue。

6.写在最后

(3)javascript的执行和运行

console.log('代码执行结束');

注意:process.nextTick 优先级大于 promise.then;浏览器中未支持 process.nextTick

 

遇到 Promise , new Promise 直接执行,输出7。 then 被分发到微任务Event Queue中。我们记为 then1 。

文的目的就是要保证你彻底弄懂javascript的执行机制,如果读完本文还不懂,可以揍我。

微任务和宏任务还有很多种类,比如 setImmediate 等等,执行都是有共同点的,有兴趣的同学可以自行了解。

 

JS执行周期 浏览器(1 7 6 8      2 4 3 5      9 11 10 12)

直接执行 new Promise ,输出11。

遇到 process.nextTick() ,其回调函数被分发到微任务Event Queue中。我们记为 process1 。

答案是不会的, setTimeout(fn,0) 的含义是,指定某个任务在主线程最早可得的空闲时间执行,意思就是不用再等多少秒了,只要主线程执行栈内的同步任务全部执行完成,栈为空就马上执行。举例说明:

事件循环是js实现异步的一种方法,也是js的执行机制。

micro-task(微任务):Promise,process.nextTick

彩世界网址 8

micro-task(微任务) Promise,process.nextTick

说明(优先级由上往下) main(主线程任务) 由上往下顺序执行

},3000)

好啦,整体代码script作为第一个宏任务执行结束,看看有哪些微任务?我们发现了 then 在微任务Event Queue里面,执行。

本文由时时app平台注册网站发布于web前端,转载请注明出处:JavaScript运维机制【彩世界网址】

关键词: