JS异步编程学习笔记一:JavaScript的事件循环机制

107 阅读2分钟

1、介绍

JavaScript是一种单线程(从上到下、从左到右执行)语言,一个时间节点只能做一件事。

单线程缺点:比如发送http请求时,等待数据返回之前网络便会出现假死的情况,因为javascript在同一个时间只能做一件事,这就导致了页面渲染和数据的执行在这个过程无法执行。但实际开发种我们并没有见过。

2、同步异步

同步【阻塞】和异步【非阻塞】的执行模式 的出现 很好的解决了单线程的诟病。

同步:严格按照单线程模式从上到下执行,执行过程中遇到问题(例如死循环)便不会执行下边的代码,造成阻塞

异步:js从上到下执行,遇到异步代码会将其挂起,等到所有的同步代码执行完毕,再执行挂起的异步代码

3、递归

函数内嵌套函数,海量函数时会出现栈溢出存在风险问题

如何防止出现栈溢出现象呢? 可以将内部调用的函数采用异步调用的方式。

//无限递归,发生栈溢出(栈的深度根据浏览器引擎和js引擎有区别)
var i = 0
function task(){
    i++
    console.log(`递归了${i}次`)
    task()
}
task()
//通过异步调用改写后
var i = 0
function task(){
    i++
    setTimeout(()=>{
        console.log(`递归了${i}次`)
        task()
    },0)
}
task()

4、宏任务和微任务

js中存在两种异步任务:宏任务与微任务

宏任务: 包括setTimeoutsetIntervalAJAX等,

微任务:随着ECMA升级提出的新的异步任务,例如 Promise.thenPromise.then 。每一个宏任务执行前,会检测当前事件循环中是否含有未执行的微任务,如果有则首先执行微任务,执行完本次微任务后再执行下一个宏任务。每个宏任务内部都可注册当次任务的微任务队列,在下一个宏任务执行前运行。

5、经典笔试题

setTimeout(function() {console.log('timer1')}, 0)      //宏任务1

requestAnimationFrame(function(){                                //宏任务
 console.log('UI update')
})

setTimeout(function() {console.log('timer2')}, 0)      //宏任务2

new Promise(function executor(resolve) {
 console.log('promise 1')         //同步1
 resolve()
 console.log('promise 2')         //同步2
}).then(function() {
 console.log('promise then')           //微任务1
})

console.log('end')                //同步3

//结果1:  promise 1 、promise 2 、end  、promise then 、timer1、timer2 、UI update
//结果2:  promise 1 、promise 2 、end  、promise then 、UI update 、timer1、timer2 
原因:requestAnimationFrame的执行频率要参考浏览器的刷新频率
document.addEventListener('click', function(){    //宏任务
 Promise.resolve().then(()=> console.log(1));         //微任务会在下一个宏任务之前执行
 console.log(2); //同步
})

document.addEventListener('click', function(){    //下一个宏任务
 Promise.resolve().then(()=> console.log(3));
 console.log(4); //同步
})

//结果  2、1、4、3
原因:事件任务也是异步任务,并且是宏任务。微任务会在下一个宏任务触发之前执行。