1.js的由来
JS 创始人 Brendan Eich
- 主要是解决当时表单提交的时候一些校验处理,因为当年带宽比较小,如果没有校验每次重复提交很浪费资源。
- js是为浏览器服务的,浏览器统一调度js
js文档: 不包括event loop
html文档:反而包含event loop
事件循环描述: 浏览器用于协调用户交互包括:鼠标,键盘,JS,渲染,网络等行为的机制。 js没有权限去获取系统的时间,都是被动的,依赖与浏览器 大白话:js没有事件循环,是浏览器的通过事件循环-控制了不同的事件源的交互。
2.异步
大部分事件源都是异步的,而且是队列形式-排队执行。
2.1. 两种队列
- 宏队列:宏的都是事件,宏每次只执行一次,串行执行 浏览器的各个事件的队列 用户交互(鼠标,键盘) dom操作(页面渲染) 定时器(settimeout) 网络请求(ajax) historyAPI
- 微队列 js微执行的任务队列 promise mutaionObsve object.observe已废弃
2.2.执行流程
1.一次宏任务
2.清空所有微任务
3.Html渲染
4.回到1
注意:每一个<script></script>都是一个事件源宏队列,如果有多个 按顺序执行宏队列
<html>
<script >
console.log("aaa")
Promise.resolve().then(() => {
console.log("111")
})
</script>
<script >
console.log("bbb")
Promise.resolve().then(() => {
console.log("222")
})
</script>
</html>
2.3.从js的执行栈分析宏队列与微队列关系
- 每次左边执行栈都会加入由宏任务和微任务的方法到 执行栈里。
- 添加方式:宏现任先添加最前面一个,然后再添加所有微任务。
- 执行流程:先执行放入到执行栈里的宏任务,再执行所有微任务
注意:在代码的根地方console.log()输出也是一种宏任务
栗子
console.log("1")
setTimeout(() => {
console.log("2")
}, 0);
Promise.resolve().then( () => {
console.log("3")
}).then( () => {
console.log("4")
})
console.log("5")
//1
//5
//3
//4
//2
3.在node.js的事件循环
1.原理
node.js 中 js 依附于libuv ,取消所有宏输入(如键盘,鼠标),只保留了宏队列和微队列,新增I/O
2.与浏览器区别
- 事件循环的过程没有 HTML 渲染。只剩下了宏队列和微队列这两个部分。
- 宏队列的事件源不同。Node.js 端没有了⿏标等外设但是新增了⽂件等 IO。
3.不同版本-执行差异
node.js 10版本 11版本 或者之前的版本 eventloop事件是执行完所有相同类型宏事件,再执行微事件
node.js 12版本 eventloop事件是跟浏览器一致,一次只执行一个宏事件,同时清空宏下的所有微事件
4.栗子
setTimeout(() => {
console.log("settimeout 1")
Promise.resolve().then(() => {
console.log("promise 1")
})
}, 0);
setTimeout(() => {
console.log("settimeout 2")
Promise.resolve().then(() => {
console.log("promise 2")
})
}, 0);
setImmediate(() => {
console.log("setImmediate 3")
Promise.resolve().then(() => {
console.log("promise 3")
})
}, 0);
setImmediate(() => {
console.log("setImmediate 4")
Promise.resolve().then(() => {
console.log("promise 4")
})
}, 0);
// node.js 12版本
// eventloop事件是跟浏览器一致,一次只执行一个宏事件,同时清空宏下的所有微事件
// setImmediate 优于setTimeout先执行
// setImmediate 3
// promise 3
// setImmediate 4
// promise 4
// settimeout 1
// promise 1
// settimeout 2
// promise 2
// node.js 10版本
// eventloop事件是执行完所有相同类型宏事件,再执行微事件
// setImmediate 3
// setImmediate 4
// settimeout 1
// settimeout 2
// promise 3
// promise 4
// promise 1
// promise 2
4.新方法 setImmediate
setImmediate 是node.js 新增的独有方法
#解决setTimeOut精度问题
//这里设置的0秒是有误差的,因为循环是依赖浏览器执行
setTimeout(()=> {
},0)
setImmediate 优于setTimeout 立即执行
setTimeout(() => {
console.log('timeout');
}, 0)
setImmediate(() => {
console.log('immediate')
})
//immediate
//timeout
5.process.nextTick
这个函数其实是独立于 Event Loop 之外的,它有一个自己的队列,当每个阶段完成后,如果存在 nextTick 队列,就会清空队列中的所有回调函数,并且优先于其他 microtask 执行。
setTimeout(() => {
console.log('timer1')
Promise.resolve().then(function () {
console.log('promise1')
})
//process.nextTick 也优先于微任务
process.nextTick(() => {
console.log('nextTick1')
})
}, 0)
//process.nextTick 优先于宏任务
process.nextTick(() => {
console.log('nextTick2')
process.nextTick(() => {
console.log('nextTick3')
})
})
// nextTick2
// nextTick3
// timer1
// nextTick1
// promise1
promise async await
Promise是一个构造函数,调用的时候会生成Promise实例。当Promise的状态改变时会调用then函数中定义的回调函数。我们都知道这个回调函数不会立刻执行,他是一个微任务会被添加到当前任务队列中的末尾,在下一轮任务开始执行之前执行。
async/await成对出现,async标记的函数会返回一个Promise对象,可以使用then方法添加回调函数。await后面的语句会同步执行。但 await 下面的语句会被当成微任务添加到当前任务队列的末尾异步执行。
async function async1(){
console.log('async1 start')
await async2() //这里等价于同步代码,里面如果有promise 也是优先于下面全局的 promise先调用。
console.log('async1 end') //这里的代码相当与包装成 then 执行,会当微任务放入微任务队列里。等待上面的所有同步和异步都执行完。
}
async function async2(){
console.log('async2')
}
console.log('script start')
setTimeout(function(){
console.log('setTimeout')
},0)
async1();
new Promise(function(resolve){
console.log('promise1')
resolve();
}).then(function(){
console.log('promise2')
})
console.log('script end')
//node 12输出内容
// script start
// async1 start
// async2
// promise1
// script end
// async1 end
// promise2
// setTimeout
巩固练习
console.log('script start')
async function async1() {
console.log('async1 111')
await async2() //注意在await 后面的任务都会到最后 才放入微任务队列,等最后才执行
await async3() //必须等待上面的微任务结束后,才执行这里的代码
console.log('async1 222')//这里会放到最后当微任务执行
}
async function async2() { //当前的promise的每一个then会和全局的promise的then 一起按顺序交替调用
console.log('async2 111')
return Promise.resolve().then(()=>{
console.log('async2 222')
}).then(()=>{
console.log('async2 333')
}).then(()=>{
console.log('async2 444')
})
}
async function async3() {
console.log('async3 111')
return await Promise.resolve().then(()=>{
console.log('async3 222')
}).then(()=>{
console.log('async3 333')
}).then(()=>{
console.log('async3 444')
})
}
async1()
setTimeout(function() {
console.log('setTimeout')
}, 0)
new Promise(resolve => {
console.log('Promise 111') //这里是定义 同步代码
resolve()
})
.then(function() {
console.log('Promise 222')
})
.then(function() {
console.log('Promise 333')
})
console.log('script end')
// script start
// async1 111
// async2 111
// Promise 111
// script end
// async2 222
// Promise 222
// async2 333
// Promise 333
// async2 444
// async3 111
// async3 222
// async3 333
// async3 444
// async1 222
// setTimeout