命令
SetTimeout()函数:用于指定某个函数或代码执行后的毫秒数。它返回一个整数,表示计时器的数量,可用于取消计时器。
例子
console . log(1);setTimeout(function(){ console . log(2);}, 0);console . log(3);问:最终印刷顺序是什么?(如果不了解js的运行机制,就会得到错误的答案。)
正确答案:1 3 2
分析:不管setTimeout的执行时间是0还是1000,结果都是先输出3,再输出2。这是面试官经常考察的js运行机制问题。接下来,我们将介绍一个概念,即JavaScript是单线程的。
二、JavaScript单线程
JavasScript引擎基于事件驱动和单线程执行。JS引擎一直在等待任务队列中任务的到来,然后处理它们。任何时候,浏览器中只有一个JS线程在运行程序,也就是主线程。
一般来说,JS一次只能做一件事,也就是常说的“阻塞执行”。
任务排队
那么单线程JavasScript是如何实现“非阻塞执行”的呢?
答:异步很容易实现无阻塞,所以异步是JavaScript中耗时操作或者时间不确定操作的必然选择。如何实现事件点击触发回调函数、ajax通信、定时器等异步处理?
答:任务队列
所有的任务可以分为两种类型,一种是同步的,另一种是异步的。
任务队列:存储各种事件和任务的先进先出队列。
同步任务
同步任务:在主线程上排队等待执行的任务,下一个任务只能在前一个任务执行后才能执行。
输出如:console.log()变量的声明同步函数:如果调用者在函数返回时可以得到预期的返回值或者看到预期的效果,那么这个函数就是同步的。异步任务
SetTimeout和setInterval DOM事件承诺进程。nexttickfs.readfilehttp.get异步函数:如果调用方在函数返回时无法得到预期的结果,但需要在未来通过某种方式得到,那么这个函数就是异步的。另外,任务队列分为宏任务和微任务,在ES5标准中分别称为任务和作业。
宏任务
I/o settimeout setinterval setimmute request animation frame微任务
进程的宏任务和微任务的执行顺序。下一个承诺。然后突变观察者
在一个事件周期中,首先执行宏任务队列中的一个任务,然后执行微任务队列中的所有任务,然后执行宏任务队列中的下一个宏任务。
注意:当前微任务未完成时,不会执行下一个宏任务。
三.设置超时运行机制
setTimeout和setInterval的操作机制是将指定的代码移出此执行,然后检查指定的时间是否到了下一轮事件循环。如果到达,执行相应的代码;如果没有,等到下一轮事件循环再做新的判断。
这意味着,在本次执行的所有同步代码执行完毕之前,setTimeout指定的代码不会执行。
优先级关系:要挂起异步任务,先执行同步任务,同步任务执行后再响应异步任务。
第四,先进
console . log(' A ');setTimeout(函数(){ console . log(' B ');}, 0);而(1) {}让我们猜猜这个程序的输出会是什么。
答:答。
注意:建议先注释掉while循环代码块的代码,执行后删除进程,否则会造成“假死”。
在同步队列输出一个后,它会陷入while(true){}的无限循环,异步任务将不会被执行。
同样的,有时候addEventListener()方法监听click事件click,用户点击一个按钮就卡住了,因为JS当前正在处理同步队列,所以click触发器事件不能放入执行栈,也不会被执行,导致“假死”。
5.定期获取接口更新数据
for(var I=0;i4;I){ setTimeout(function(){ console . log(I));}, 1000);}输出结果是,1s: 4 4 4 4后一起输出
for循环是一个同步任务。为什么连续输出四个4?
答:因为队列插入时间,即使执行时间从1000变为0,仍然输出4个4。
那么这个问题是如何产生和解决的呢?请继续阅读
异步队列的执行时间
当异步任务被执行时,它会被直接放入异步队列吗?
答案不一定。
因为浏览器有一个计时器模块,所以在计时器到达执行时间之前,异步任务不会被放入异步队列。在for循环的执行过程中,SetTimeout没有被放入异步队列,而是被交给了计时器模块。4个循环的执行速度非常快(不到1毫秒)。当定时器到达设定时间时,setTimeout语句不会被放入异步队列。
即使setTimeout设置的执行时间为0毫秒,也计算为4毫秒。
这解释了为什么标题“什么会连续输出四个4?”
HTML5标准规定了setTimeout()第二个参数的最小值,即最短间隔不得小于4毫秒。如果低于该值,将自动增加。在此之前,在旧浏览器中最短的时间间隔被设置为10毫秒。
用闭包实现setTimeout间歇调用
for(设I=0;i4;i ) {(函数(j) { setTimeout(函数(){ console . log(j));},1000 * I)})(I);}执行后,每1秒输出一个值,分别是:0 1 2 3
这个方法使用IIFE声明,即执行的函数表达式来解决闭包带来的问题。ES6语法用于将var改为let。这里也可以使用setInterval()方法实现间歇调用。
请参见:设置超时和设置间隔之间的区别
在JS中使用基本类型的参数传递是通过值传递的特性来实现的
var output=function(I){ setTimeout(function(){ console . log(I);},1000 * i)}for(让I=0;i4;i ) {输出(I);}执行后,每1秒输出一个值,分别是:0 1 2 3
实现原理:复制传递的I值。
基于承诺的解决方案
const tasks=[];const output=(I)=new Promise((resolve)={ setTimeout(()={ console . log(I));resolve();},1000 * I);});//为(var i=0)生成所有异步操作;i5;i ) { tasks.push(输出(I));}//同步操作完成后,输出最后一个ipromise.all (tasks)。然后(()={ settimeout(()={ console . log(I);},1000)}),每1秒输出一个值,即0 1 2 3 4 5。
优点:提高了代码的可读性。
注意:如果不处理拒绝承诺,错误将被扔进黑洞。
在ES7中使用asyncaware功能的解决方案(推荐)
const sleep=(time mounts)=new Promise((resolve)={ setTimeout(resolve,time mounts);});(async ()={//断言是异步为(var I=0;i5;i ) {等待睡眠(1000);console . log(I);}等待睡眠(1000);console . log(I);})();执行后,每1秒输出一个值:0 1 2 3 4 5
不及物动词事件循环事件循环
主线程从任务队列中读取事件,这个过程是无止境的,所以整个运行机制也被称为Event Loop。
有时setTimeout显然是用3秒的延迟写的,但实际上执行该函数需要5到6秒。原因是什么?
答:setTimeout不能保证执行时间。能否及时执行取决于JavaScript线程是拥挤还是空闲。
浏览器的JS引擎遇到setTimeout,它不会在被取走后立即放入异步队列。同步任务执行后,定时器模块将在设定时间后放入异步队列。js引擎发现同步队列中没有要执行的内容,也就是说,当运行栈为空时,它从异步队列中读取并将其放入运行栈中执行。因此,setTimeout可能有更多时间等待线程。
此时,setTimeout函数体成为运行栈中的执行任务。当运行栈为空时,监听异步队列中是否有要执行的任务。如果有任务,继续执行。如果是这样,它被称为事件循环。
七.摘要
JavaScript通过事件循环和浏览器线程协调实现异步。同步可以保证顺序一致,但容易造成阻塞;异步可以解决阻塞问题,但会改变顺序。
梳理知识点:
了解JS单线程的概念:在一段时间内做一件事了解任务队列:同步任务,异步任务,了解Event Loop,了解哪些语句会放入异步任务队列,了解语句放入异步任务队列的时机。最后,希望大家看完之后有所收获。
好了,这就是本文的全部内容。希望本文的内容对大家的学习或工作有一定的参考价值。谢谢你的支持。