js同步异步 事件循环 宏微任务理解

javascript是一门 单线程 的 非阻塞 的脚本语言

单线程:javascript代码在执行的任何时候,都只有一个主线程来处理所有的任务。

非阻塞:当代码需要进行一项异步任务(无法立刻返回结果,需要花一定时间才能返回的任务,如I/O事件)的时候,主线程会挂起(pending)这个任务,然后在异步任务返回结果的时候再根据一定规则去执行相应的回调。

单线程的必要性

如果javascript是多线程的,那么当两个线程同时对dom进行一项操作,例如一个向其添加事件,而另一个删除了这个dom,那怎么处理呢?

但是单线程在保证了执行顺序的同时也限制了javascript的效率,于是开发除了web worker技术,这项技术号称让javascript成为一门多线程语言。但是web worker技术开的多线程有很多的限制。所有新线程都受主线程的完全控制,不能独立执行。其实也就说,并不是真正意义上的多线程,这些“线程” 实际上应属于主线程的子线程。并且这些子线程并没有执行I/O操作的权限,只能为主线程分担一些诸如计算等任务。也就是说这些线程并没有完整的功能,也因此这项技术并非改变了javascript语言的单线程本质。

总结一下

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

如何实现非阻塞的呢

按理说同步是阻塞模式,异步是非阻塞模式。既然js是单线程,那么js任务就要一个一个顺序执行。如果一个任务耗时过长,那么后一个任务也必须等着。woc!那它应该是阻塞模式啊,那他是如何实现的非阻塞呢?就是通过event loop(事件循环)。

js任务分为两类 同步任务 异步任务

js代码执行时,同步和异步任务分别进入不同的执行"场所",同步的进入主线程,异步的进入Event Table并注册函数。当事件完成之后event table会将这个函数移入event queue(事件队列)【英文不好 以下都会使用中文】当主线程执行栈的任务先执行,js引擎中存在的(monitoring process进程) 监视进程持续不断的检查主线程执行栈是否为空,一旦为空就会去事件队列中检查那里是否有等待调用的函数。这个过程不断的重复 ,被叫做 Event Loop (事件循环)。

执行栈与事件队列

js中不同的变量存于内存中的不同位置:堆(heap)和栈(stack)中来加以区分。其中,堆里存放着一些对象。而栈中则存放着一些基础类型变量以及对象的指针。 但是我们这里说的执行栈和上面这个栈的意义并不同。

调用一个方法的时候,js会生成一个与这个方法对应的执行环境(执行上下文)。这个执行环境中存在着这个方法的作用域,方法的参数,这个作用域中定义的变量以及这个作用域的this对象。 而当一系列方法被依次调用的时候,因为js是单线程的,同一时间只能执行一个方法,于是这些方法被排队在一个单独的地方。这个地方被称为执行栈。

当脚本执行时,js引擎解析这段代码,并将其中的同步代码按照执行顺序加入执行栈中,然后从头开始执行。如果当前执行的是一个方法,那么js会向执行栈中添加这个方法的执行环境,然后进入这个执行环境继续执行其中的代码。当这个执行环境中的代码 执行完毕并返回结果后,js会退出这个执行环境并把这个执行环境销毁,回到上一个方法的执行环境。这个过程反复进行,直到执行栈中的代码全部执行完毕。

同步代码的执行:js方法的执行会向执行栈中加入这个方法的执行环境,一个方法执行会向执行栈中加入这个方法的执行环境,在这个执行环境中还可以调用其他方法,甚至是自己,其结果不过是在执行栈中再添加一个执行环境。这个过程可以是无限进行下去的,除非发生了栈溢出,即超过了所能使用内存的最大值。

异步代码的执行:(如发送ajax请求数据)执行后会如何呢?前文提过,js的另一大特点是非阻塞,实现这一点的关键在于下面要说的这项机制——事件队列(Task Queue)。

js引擎遇到一个异步事件后并不会一直等待其返回结果,而是会将这个事件挂起,继续执行执行栈中的其他任务。当一个异步事件返回结果后,js会将这个事件加入与当前执行栈不同的另一个队列,我们称之为事件队列。被放入事件队列不会立刻执行其回调,而是等待当前执行栈中的所有任务都执行完毕, 主线程处于闲置状态时,主线程会去查找事件队列是否有任务。如果有,那么主线程会从中取出排在第一位的事件,并把这个事件对应的回调放入执行栈中,然后执行其中的同步代码...,如此反复,这样就形成了一个无限的循环。这就是这个过程被称为“事件循环(Event Loop)”的原因。

宏任务(macro task)和微任务(micro task)

现在我们已经知道异步事件会被js列到事件队列中去,实际上根据这个异步事件的类型,这个事件实际上会到对应的宏任务队列或者微任务队列中去。执行栈为空时,主线程会查看微任务队列是否有事件存在。如果不存在,那么再去宏任务队列中取出一个事件并把对应的回到加入当前执行栈;如果存在,则会依次执行队列中事件对应的回调,直到微任务队列为空,然后去宏任务队列中取出事件,把对应的回调加入当前执行栈,进入循环。当当前执行栈执行完毕时会立刻先处理所有微任务队列中的事件,然后再去宏任务队列中取出一个事件。同一次事件循环中,微任务永远在宏任务之前执行。

宏任务:

  • setInterval()
  • setTimeout()

微任务:

  • promise.then()
  • Async/Await(实际就是promise)
  • new MutaionObserver()
同步和异步

一个人边吃饭,边看手机,边说话,就是异步处理的方式。

同步处理,说话后在吃饭,吃完饭后在看手机,必须等待上一件事完了,才执行后面的事情。

所以大概可以说同步是阻塞模式,异步是非阻塞模式。

异步虽然好 但是有些问题是必须要用同步用来解决,比如有些东西我们需要的是拿到返回的数据在进行操作的。这些是异步所无法解决的。

如果我整理的文章能对大家有些许帮助  我将不胜荣幸

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值