核心概念:
1.js是单线程的
2.浏览器当中的eventloop
3.nodejs中的eventloop
js是单线程的,如果有复杂耗时的操作怎么办?有可能会导致页面卡死
eventloop就是为了解决这些问题,它的核心是异步队列,把耗时的任务放在异步队列中执行,执行完成之后再去唤起主线程
异步队列存在栈中,同步任务存在队列中,当队列中的任务执行完之后就会从栈中取出任务放在队列中执行
队列是先进先出,栈是后进先出
异步队列分为宏任务队列和微任务队列
任务执行顺序:
先执行宏任务(整个script就是一个宏任务)=》遇到promise(微任务)放入微任务队列里继续执行=》直到把所有的同步任务完成之后去执行微任务队列=》微任务队列执行完之后执行下一个宏任务
宏任务:script,settimeout,setinterval,requestAnmationFrame
微任务:promise,mutationObserver(async await实质上可以装换为promise,也是微任务)
具体实例:
案例一
执行结果:
分析:
1.宏任务: setTimeout
2.微任务:promise构造函数中是同步的状态,then也是一个promise,是一个微任务
3.执行顺序:同步:promise init ,promise end,微任务:promise result:1,宏任务:timeout
案例二
执行结果:
1.settimeout1放在宏任务队列中
2.promise微任务执行 promise2
3.settimeout2宏任务放在宏任务队列中
4,.从宏任务队列中取出settimeout1执行timeout1
5.执行微任务promise1
6.执行宏任务timeout2
案例三
执行结果:
分析:
1.await默认返回一个promise,await相当于被包裹在resolve中
2.上面的代码等价于
fn的then方法中打印了res,按理说会打印出return的一个对象,但是打印出了1234 ,这是为什么呢?
3.promise规范中约定如果有then方法并且是对象,就会当成promise处理
案例四
分析:
1.async1 start是同步代码
2.执行async2,返回一个promise,asyn2也是一个同步任务
3.async1 end是微任务,script是同步任务,优先级更高,所以会先打印script
上面的代码等同于
案例五
执行结果:
分析:
1.执行同步的script start
2.执行async1,会打印async1 start
3.new Promise 构造函数中是同步执行的,会打印promise1
4.promise没有resolve方法,导致promise中的函数一直都不会完成,所以后面的代码都不会执行了
案例六
执行结果:
分析:
1.执行同步script start
2.settimeout加入宏任务队列
3.执行async1,打印同步的async1 start
4.等待async2执行完,打印async2,返回一个promise,打印async1 end 进入微任务队列
5.执行同步任务promise
6.then中的加入微任务队列
7.执行同步的script end,至此,第一轮宏任务已经完后
8.读取微任务列表,执行完成,即执行打印async1 end ,promise2,promise3,promise4,微任务队列执行完毕
9.执行下一个宏任务settimeout,打印settimeout
案例七
执行结果:
分析:
1.setTimeout放在宏任务队列中
2.执行async1,将then放在微任务队列中
3...
执行结果:
分析:
1.resolve是promise,每个promise都是thenable对象,都有then方法,resolve处理thenable也会包裹一层promise
2.async2是一个promise所以async2这相当于有两层promise,就会推迟两次执行
执行结果:
分析:
1.async2打印这里不是一个promise,只会推迟执行一次
2.如果async中只return一个基本类型,那么执行结果和最初的执行结果一样
nodejs中的eventloop
js应用程序经过v8的编译之后经过node的api执行libuv
libuv就是eventloop完成的,libuv中有工作队列,当有任务完成时会触发一个callback回到nodeapi然后回到应用程序
定时器阶段=》循环回调=》内部使用的一些钩子函数=》poll阶段(监听)执行所有异步任务的io=》check节点(setimmediate)
nexttick在当前任务完成之后下一个阶段任务开始之前执行
nodejs中的任务队列
宏任务:settimeout,setinterval,setimmediate,io
微任务:promise,pross.nextTick(优先级比promise高)
案例一
多执行几次之后发现settimeout和setImmediate执行先后是不固定的,为什么会产生这种情况呢?
settimeout是在timer阶段执行的,而setimmediat是在check阶段。如果在poll阶段timer没有加入到执行队列中时,就先执行check阶段,等下一个轮询再执行timer。如果已经加入到执行队列中,就会先执行settimeout,程序执行也是有耗时的。
案例二
执行结果:
setimmediate始终在settimeout执行之前
分析:
由于事件都是在poll阶段注册的,所以在poll阶段timer是没有时间到期的,所以在执行完poll阶段会马上执行check阶段,等下一个时钟周期才会触发timer
案例三
nodejs都是基于回调式的写法,nodejs的api回调的第一个参数都是error,第二个值才是data。那么是怎么抛出错误的呢?
一般是使用上面的方式,当参数arg的类型不正确的时候就在下个时钟周期的时候执行callback抛出异常,这样不会因为一个错误影响程序的运行
案例四
执行结果:
每个阶段尾部执行nextTick,setTmmediate是在check阶段触发的时候执行,所以nextTick会先执行
不同版本中的Eventloop是不同的
案例一
在node11版本之后会先打印time1再打印promiss1,time2,promiss2
在node11之前会打印time1,time2,promiss1,promiss2
timer阶段不同版本执行时机的变化:
node11之前先执行所有的宏任务再执行微任务,之后是先把当前任务的所有内容执行完再执行下个任务
案例二
check阶段不同版本的变化:
node11之前:immediate1 immediate2 immediate3 immediate4 promise resolve
node11之后: immediate1 immediate2 promise resolve immediate3 immediate4
11之后先执行宏任务然后执行宏任务的微任务,然后再执行下一个宏任务
案例三
node11之后
node11之前next tick是在最后执行