js中的同步和异步,宏任务和微任务,async和await
所有的任务分为两种,⼀种是同步任务,⼀种是异步任务。
同步任务指的是,在主线程上排队执⾏的任务,只有前⼀个任务执⾏完毕,才能执⾏后⼀个任务;
异步任务指的是,不进⼊主线程、⽽进⼊"任务队列"(task queue)的任务,只有等主线程任务执⾏完毕,"任务队列"开始通知主线程,请求执⾏任务,该任务才会进⼊主线程执⾏。这⾥说到了⼀个“队列”(即任务队列),该队列放的是什么呢,放的就是setTimeout中
的function,这些function依次加⼊该队列,即该队列中所有function中的程序将会在该队列以外的所有代码执⾏完毕之后再以此执⾏,这是为什么呢?因为在执⾏程序的时候,浏览器会默认setTimeout以及ajax请求这⼀类的⽅法都是耗时程序(尽管可能不耗时),将其加⼊⼀个队列中,该队列是⼀个存储耗时程序的队列,在所有不耗时程序执⾏过后,再来依次执⾏该队列中的程序。具体来说,异步运⾏机制如下:
(1)所有同步任务都在主线程上执⾏,形成⼀个执⾏栈(execution context stack)。
(2)主线程之外,还存在⼀个"任务队列"(task queue)。只要异步任务有了运⾏结果,就在"任务队列"之中放置⼀个事件。
(3)⼀旦"执⾏栈"中的所有同步任务执⾏完毕,系统就会读取"任务队列",看看⾥⾯有哪些事件。那些对应的异步任务,于是结束等待状态,进⼊执⾏栈,开始执⾏。
(4)主线程不断重复上⾯的第三步。
setTimeOut函数为异步任务,for循环为同步任务,setTimeOut⾥的函数为回调函数。执⾏顺序为:同步优先,异步靠边,回调垫底。所以即使setTimeOut的时间参数是0依然会放到任务队列⾥,⽽不是主线程。主线程执⾏完for循环以后才执⾏异步任务setTimeOut。另外setTimeout()只是将事件插⼊了"任务队列",必须等到当前代码(执⾏栈)执⾏完,主线程才会去执⾏它指定的回调函数。要是当前代码耗时很长,有可能要等很久,所以并没有办法保证,回调函数⼀定会在setTimeout()指定的时间执⾏。
javascript是单线程。单线程就意味着,所有任务需要排队,前⼀个任务结束,才会执⾏后⼀个任务。如果前⼀个任务耗时很长,后⼀个任务就不得不⼀直等着。于是就有⼀个概念——任务队列。如果排队是因为计算量⼤,CPU忙不过来,倒也算了,但是很多时候CPU是闲着的,因为IO设备(输⼊输出设备)很慢(⽐如Ajax操作从⽹络读取数据),不得不等着结果出来,再往下执⾏。于是JavaScript语⾔的设计者意识到,这时主线程完全可以不管IO设备,挂起处于等待中的任务,先运⾏排在后⾯的任务。等到IO设备返回了结果,再回过头,把挂起的任务继续执⾏下去。
JavaScript的单线程,与它的⽤途有关。作为浏览器脚本语⾔,JavaScript的主要⽤途是与⽤户互动,以及操作DOM。这决定了它只能是单线程,否则会带来很复杂的同步问题。⽐如,假定JavaScript同时有两个线程,⼀个线程在某个DOM节点上添加内容,另⼀个线程删除了这个节点,这时浏览器应该以哪个线程为准?
所以,为了避免复杂性,从⼀诞⽣,JavaScript就是单线程,这已经成这门语⾔的核⼼特征,将来也不会改变。
注:所谓单线程,是指在JS引擎中负责解释和执⾏JavaScript代码的线程只有⼀个。
总结:计算机中的同步就是排队等待,假如你是第⼀百零⼀个备胎,那你只能等前⾯的⼀百个爆了之后才能‘处理'你。异步就是,尽管你是第⼀百零⼀个,她还是能照顾到你的感受。
js是同步的?是的,单线程,那肯定只能同步(排队)执⾏咯
js为什么需要异步?如果JS中不存在异步,只能⾃上⽽下执⾏,万⼀上⼀⾏解析时间很长,那么下⾯的代码就会被阻塞。对于⽤户⽽⾔,阻塞就意味着"卡死",这样就导致了很差的⽤户体验
例:
也就是说,setTimeout⾥的函数并没有⽴即执⾏,⽽是延迟了⼀段时间,满⾜⼀定条件后,才去执⾏的,这类代码,我们叫异步代码。
总结:同步可以保证顺序⼀致,但是容易导致阻塞;异步可以解决阻塞问题,但是会改变顺序性,根据不同的需要去写你的代码。
Promise是异步的,是指他的then()和catch()⽅法,Promise本⾝还是同步的,所以这⾥先执⾏a变量内部的Promise同步代码。(同步优先)
注意new Promise() 是同步⽅法,resolve才是异步⽅法。
同步(Promise)>异步(微任务(Tick,Promises.then, Promise.catch,resove,reject,MutationObserver)>宏认为
(setTimeout,setInterval,setImmediate))
同步代码执⾏完成后,才会再去执⾏异步,哪怕异步已经到了执⾏的时间了。
JavaScript的任务分为微任务(Microtasks)和宏任务(task);
宏任务是主流,当js开始被执⾏的时候,就是开启⼀个宏任务,在宏任务中执⾏⼀条⼀条的指令;
宏任务可以同时有多个,但会按顺序⼀个⼀个执⾏;
每⼀个宏任务,后⾯都可以跟⼀个微任务队列,如果微任务队列中有指令或⽅法,那么就会执⾏;如果没有,则开始执⾏下⼀个宏任务,直到所有的宏任务执⾏完为⽌,微任务相当于宏任务的⼩尾巴;
为什么有了宏任务,还会有微任务存在?因为宏任务太占⽤性能,当需要⼀些较早就准备好的⽅法,排在最后才执⾏的时候,⼜不想新增⼀个宏任务,那么就可以把这些⽅法,⼀个⼀个的放在微任务队列⾥⾯,在这个宏任务中的代码执⾏完后,就会执⾏微任务队列。
[宏任务:macro task]
- 定时器
- 事件绑定
- ajax
- 回调函数
- Node中fs可以进⾏异步的I/O操作
[微任务:micro task]
- Promise(async/await) => Promise并不是完全的同步,当在Excutor中执⾏resolve或者reject的时候,此时是异步操作,会先执⾏then/catch等,当主栈完成后,才会再去调⽤resolve/reject把存放的⽅法执⾏
- Tick (node中实现的api,把当前任务放到主栈最后执⾏,当主栈执⾏完,先执⾏nextTick,再到等待队列中)
- MutationObserver (创建并返回⼀个新的 MutationObserver 它会在指定的DOM发⽣变化时被调⽤。)
任务队列到达时间后先进先出的原则
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。
发表评论