【前端】探秘JavaScript同步异步运行机制

JavaScript单线程机制

​ 总所周知,JavaScript是一门单线程的脚本语言,所有事情必须按部就班的按照一个顺序依次执行下去,那么为什么JavaScript是一门单线程语言呢?这和他的运用场景有关和设计初衷有关,由于JavaScript的设计初衷是一门运行在客户端的脚本语言,方便浏览器与用户进行交互,通过JavaScript去操作DOM结构,那么DOM操作就必须按照一定的顺序来,如果有多线程同时操作一个DOM,那么情况会变得相当混乱。

​ 但是在处理器愈来愈强大的今天,单线程是有点大材小用,于是HTML5提出了web work标准,允许创建多个线程,但是次线程是无法操作DOM且必须完全由主线程指挥,所以其实本质上JavaScript依旧是单线程。

事件循环(Event Loop)

​ 当执行栈空闲的时候会去读取任务队列,把任务队列的事情搬到执行栈,在处理过程中如果有遇到异步代码再次挂起,执行栈空的时候再去任务队列拿,形成了一个回环,这就是事件循环 event loop,这里提到了执行栈和任务队列的概念,下文会讲到。

执行栈和任务队列(stack and heap)

​ 在JavaScript中,是一个单线程的执行过程,前一个事情没做完,后面的事情就会一直等着,但是有一些事情需要的执行时间很长(比如ajax请求),就会导致整个队列在那干等着,于是会,JavaScript创始人开放了执行栈和任务队列。

  • 一段JavaScript代码放进执行栈逐行解析,碰到异步代码,会将异步代码挂起,然后继续往下逐行解析。
    • 异步代码被挂起执行,当执行结束拿到结果的时候,会在任务队列放置一个事件。
  • 当执行栈空的时候,系统会去读取任务队列,将已经存在任务队列里面的代码全部拿进执行栈,当作同步代码继续执行。(这时候异步代码可以理解为变成同步代码,因为只有已经有结果的异步代码才会被放置到任务队列)
  • 执行栈执行这些“同步代码”(之前异步转同步的)的时候,如果又遇到了异步代码(比如定时器里面创建定时器),那么异步代码会继续挂起。

这里解释一下,定时器被挂起的时候做的事情

  • 定时器

    • 当一个定时器被挂起的一瞬间,就开始计时了,当计时完毕的时候回调函数立马扔进任务队列
    • 执行栈执行完了,去任务队列拿进执行栈
    • 例子一:
    const timer = setTimeout(function(){
    	console.log('我是计时器,5s的那个')
    },5000)
    
    const timer = setTimeout(function(){
    	console.log('我是计时器,2s的那个')
    },2000)
    
    
    ...   //这一段是计算量非常大的同步代码,计算完成需要费时10s
    
    
    //程序在运行 10s 的时候输出:
    '我是计时器,2s的那个'
    '我是计时器,5s的那个'
    
    • 例子一我们得知:

      • 定时器是一旦被执行栈侦测到,就被挂起,并且开始计时,一旦计时完成立马把回调函数扔进任务队列。

      • 因为程序执行非常快,5s的定时器和2s的定时器几乎是同时被执行到并挂起开始计时的,2s的定时器因为时间比较短,会先计时完成扔进任务队列。

      • 而当执行栈执行完毕(例子里面是用了10s),会到任务队列拿代码块进执行栈,队列先入先出的原理,所以先执行的是2s定时器的回调函数,再执行的是5s的回调函数

    • 例子二:

    const timer = setTimeout(function(){
    	console.log('我是计时器,5s的那个')
        	setTimeout(function(){
                console.log('我是在5s定时器里面创建的定时器')
            },5000)
    },5000)
    
    ... //这是一段计算量非常大的同步代码,计算完成需要费时10s
    
    //程序在运行 10s 的时候输出:
    '我是计时器,5s的那个'
    //时间又过了 5s
    '我是在5s定时器里面创建的定时器'
    
    • 例子二我们得知:
      • 当父计时器计时完成的时候被放进任务队列,此时子计时器还未创建。
      • 当执行栈执行完成的时候,到任务队列拿代码,父定时器的回调被执行,这时候子计时器才被创建
      • 可以理解为,异步的定时器计时完成回调扔进任务队列再被拿到执行栈的时候,这时候的回调函数的代码块已经是同步代码了,而回调里面的异步代码依旧是异步代码,该挂起的时候依旧要被挂起

那ajax呢?ajax代码也是一个异步代码,异步的时候发生了什么

  • 执行栈执行的时候碰到ajax代码,ok,被挂起,这时候ajax请求已经发送了出去,只不过还没有回应
  • 一旦有了回应,拿到响应体的时候,ajax执行的回调立马被扔进任务队列
  • 当执行栈执行完毕的时候,到任务队列拿代码的时候才会执行ajax响应回来的回调函数
  • 如果你有多个ajax请求呢?
    • 他们都会被挂起
    • 都是在各自挂起的一瞬间开始发送请求
    • 谁先收到响应,谁的回调先扔进任务队列

总结

​ JavaScript执行栈执行的时候遇到异步代码的时候统统给你挂起,而这些异步代码不管被挂起多少个,也不管谁先挂起谁后挂起,只不过谁先搞定,谁的回调就先扔进任务队列罢了。

  • 3
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值