美文网首页
Js既然是单线程的,怎么会有异步这种操作呢!

Js既然是单线程的,怎么会有异步这种操作呢!

作者: 李贵功 | 来源:发表于2018-08-20 21:36 被阅读0次

前言
说到js的单线程(single threaded)和异步(asynchronous),很多同学不禁会想,这不是自相矛盾么?其实,单线程和异步确实不能同时成为一个语言的特性。js选择了成为单线程的语言,所以它本身不可能是异步的,但js的宿主环境(比如浏览器,Node)是多线程的,宿主环境通过某种方式(事件驱动,下文会讲)使得js具备了异步的属性。往下看,你会发现js的机制是多么的简单高效!

说说浏览器

js是单线程语言,浏览器只分配给js一个主线程,用来执行任务(函数),但一次只能执行一个任务,这些任务形成一个任务队列排队等候执行,但前端的某些任务是非常耗时的,比如网络请求,定时器和事件监听,如果让他们和别的任务一样,都老老实实的排队等待执行的话,执行效率会非常的低,甚至导致页面的假死。所以,浏览器为这些耗时任务开辟了另外的线程,主要包括http请求线程,浏览器定时触发器,浏览器事件触发线程,这些任务是异步的。下图说明了浏览器的主要线程。

image.png

再说说任务队列

刚才说到浏览器为网络请求这样的异步任务单独开了一个线程,那么问题来了,这些异步任务完成后,主线程怎么知道呢?答案就是回调函数,整个程序是事件驱动的,每个事件都会绑定相应的回调函数,举个栗子,有段代码设置了一个定时器setTimeout(function(){ console.log(time is out);},50);

执行这段代码的时候,浏览器异步执行计时操作,当50ms到了后,会触发定时事件,这个时候,就会把回调函数放到任务队列里。整个程序就是通过这样的一个个事件驱动起来的。

所以说,js是一直是单线程的,浏览器才是实现异步的那个家伙。

说回主线程

js一直在做一个工作,就是从任务队列里提取任务,放到主线程里执行。下面我们来进行更深一步的理解。


image.png

我们把刚才了解的概念和图中做一个对应,上文中说到的浏览器为异步任务单独开辟的线程可以统一理解为WebAPIs,上文中说到的任务队列就是callback queue,我们所说的主线程就是有虚线组成的那一部分,堆(heap)和栈(stack)共同组成了js主线程,函数的执行就是通过进栈和出栈实现的,比如图中有一个foo()函数,主线程把它推入栈中,在执行函数体时,发现还需要执行上面的那几个函数,所以又把这几个函数推入栈中,等到函数执行完,就让函数出栈。等到stack清空时,说明一个任务已经执行完了,这时就会从callback queue中寻找下一个人任务推入栈中(这个寻找的过程,叫做event loop,因为它总是循环的查找任务队列里是否还有任务)。

借以解释几个容易困惑的问题

setTimeout(f1,0)是什么鬼

这个语句最大的疑问是,f1是不是立刻执行?答案是不一定,因为要看主线程内的命令是否已经执行完了,如下代码:setTimeout(function(){console.log(1);},0);console.log(2);

这段代码的输出结果是2,1。因为执行setTimeou后,会立即把匿名函数放到callback queue里面等待主线程的召唤,但这个时候stack里面并不是空的,因为还有一句console.log(2)。等到执行完console.log(2)后,才通过event loop把匿名函数放到stack里面。所以setTimeout(f1,0)这个语句并不是没有意义,如果f1是很耗时的任务,那就应该把任务放到callback queue里面,等到主程序执行完后再执行。

了解完上文内容,我们就知道了,ajax请求内容的时候是异步的,当请求完成后,会触发请求完成的事件,然后把回调函数放入callback queue,等到主线程执行该回调函数时还是单线程的。
界面渲染线程是单独开辟的线程,是不是DOM一变化,界面就立刻重新渲染?如果DOM一变化,界面就立刻重新渲染,效率必然很低,所以浏览器的机制规定界面渲染线程和主线程是互斥的,主线程执行任务时,浏览器渲染线程处于挂起状态。

如何利用浏览器的异步机制

我们已经知道,js一直是单线程执行的,浏览器为几个明显的耗时任务单独开辟线程解决耗时问题,但是js除了这几个明显的耗时问题外,可能我们自己写的程序里面也会有耗时的函数,这种情况怎么处理呢?我们肯定不能自己开辟单独的线程,但我们可以利用浏览器给我们开放的这几个,浏览器定时器线程和事件触发线程是好利用的,网络请求线程不适合我们使用。

异步的好处和适合的场景

1.异步的好处

我们直接通过一个例子对同步和异步进行对比,假设有四个任务(编号为1,2,3,4),它们的执行时间都是10ms,其中任务2是任务3的前置任务,任务2需要20ms的响应时间。下面我们做下对比,你就知道怎么实现的非阻塞I/O了。


image.png

2.适合的场景

可以看出,当我们的程序需要大量I/O操作和用户请求时,js这个具备单线程,异步,事件驱动多种气质的语言是多么应景!相比于多线程语言,它不必耗费过多的系统开销,同时也不必把精力用于处理多线程管理,相比于同步执行的语言,宿主环境的异步和事件驱动机制又让它实现了非阻塞I/O,所以你应该知道它适合什么样的场景了吧!

相关文章

  • Js既然是单线程的,怎么会有异步这种操作呢!

    前言说到js的单线程(single threaded)和异步(asynchronous),很多同学不禁会想,这不是...

  • JS执行机制

    首先我们来看几个问题: 1.JS是单线程的么?2.JS有异步么?3.JS单线程怎么实现的的异步 1.JS是单线程的...

  • javascript面试准备(一)

    interview js 基础 原型 原型链 作用域 闭包 异步 单线程 js Api dom 操作 ajax 事...

  • js 异步全览

    问题! JS 为什么是单线程的? 为什么需要异步? 单线程又是如何实现异步的呢? promise 的优点是什么? ...

  • [es6]对异步问题的解决

    异步问题得到解决 : 可以获取到异步操作内的数据 异步的原理:都知道js是单线程的,所谓的单线程就是一次只能完成一...

  • 单线程异步和event-loop

    单线程异步 js是单线程异步的,单线程就是同一时间只做一件事,原因是避免dom操作的冲突,设想一下js是多线程的,...

  • Javascript 事件循环eventLoop

    都知道javascript是单线程,那么问题来了,既然是单线程顺序执行,那怎么做到异步的? 我们理解的单线程应该是...

  • Promise原理解析,使用ES5实现Promise

    Promise js的单线程特性导致大量的异步操作,异步操作我们通常使用函数回调的方式实现。大量的函数回调会产生我...

  • javascript单线程,异步与执行机制

    js的单线程模型与游览器的进程/线程息息相关,在了解js单线程与异步的时候,建议先看看这篇文章 单线程/异步 js...

  • JS异步机制

    刚开始使用JS异步的时候,有这样的疑问:JS不是单线程的吗?为什么会有异步机制?但是如果没有异步机制,定时器又是怎...

网友评论

      本文标题:Js既然是单线程的,怎么会有异步这种操作呢!

      本文链接:https://www.haomeiwen.com/subject/uskriftx.html