js_setTimeout和js单线程任务队列以及let/var声明的循环变量的作用现象推测(中断和异步)/windows(macos)鼠标指针的圈圈是个啥
declaration
这是初学者的猜测
reference link
异步JavaScript - 学习 Web 开发 | MDN (mozilla.org)
线程阻塞:通用异步编程概念 - 学习 Web 开发 | MDN (mozilla.org)
从阻塞的角度解释了令人沮丧的转圈圈体验.
并发模型与事件循环 - JavaScript | MDN (mozilla.org)
试验代码
运行环境为vscode+code runner.
代码说明
为了探究setTimeout()的执行规律
我在setTimeout()任务执行的时候追加了时间戳,用以参考
根据需要,您可以在合适的地方添加时间戳打印语句来测试
一般只需要看后5位/4位的区别.
包括对setTimeout()函数安排的一系列任务的任务执行队列的试验.
因该是说明了一些现象.(也验证了我的一些猜测).
// let cnt=0; for (var i = 0; i < 5; ++i) { // 可能是因为js是单线程的 //setTimeout()之外的语句会优先执行 //setTimeout()安排的任务会添加到js任务队列中(添加的时机取决于timeout参数),队列中的任务的执行时机则取决于该任务是否位队列中的第一个任务 setTimeout(() => console.log("for1:test insert to the task queue" + ' timeout==0'), 0); const id = setTimeout(() => console.log(i+"\tby setTimeout_handler"+ ' timestamp='+ +new Date()), 2000);//id由于断点调试监视 // cnt++; console.log(i + " \n\t(var:output by single console.log() after the setTimeout())"); if (i==4){ console.log("now i===4,the timestamp: "+ +new Date()); } } console.log("---------------------------------------------------------"); setTimeout(()=>console.log("the outer setTimeout(),test first to the task queue by setTimeout():timeout=0ms"),0); setTimeout(()=>console.log("seperate two for_setTimeout():timeout=2s"),2000); setTimeout(()=>console.log("end for_setTimeout():timeout=2.1s"),2100); for (let i = 0; i < 5; ++i) { // 可能是因为js是单线程的,只有for执行完成后才可以执行setTimeout. //加到js任务队列中 setTimeout(() => console.log("for2:test insert to the task queue" + i + ' timeout==0'), 0); // const id_start = setTimeout(() => console.log("setTimeout in the second for ."), 2000); const id = setTimeout(() => console.log(2 * i), 2000); console.log(i + " \n\t(let:output by single console.log() after the setTimeout())"); } console.log("the last statement(in outer context(relative to setTimeout() task queue)):over time stamp :"+ +new Date()); /* 由于循环次数十分小,所以几乎可以认为for循环的执行时间接近0s 首先应该理解的是,任务的安排和任务的插入以及执行是不同的,在理解任务的安排时,不用理会setTimeout()的timeout参数(也不用理会传给setTimeout()的任务函数(称之handler),timeout体现在延时将任务插入到队列(setTimeout将handler安排完后,就开始对timeout进行倒计时,计时结束就插入任务队列,直到现在,handler要干什么都还不清楚(或者说会不会用到循环变量for里面的变量i)),此外,尽管handler进入任务队列后也不能够确认它会立即执行,如果它之前还有任务,它就得再等,这个时间是不定的. (for之类不是由计时器函数安排的任务似乎不同于由计时器函数而产生的队列任务,有优先执行的现象)handler只好等这些语句结束后再执行),任务进入执行队列后就会一个个被执行掉 而setTimeout()通过timeout参数设置延时(插入任务执行队列中) 这里for循环会执行5次,而setTimeout任务的插入队列操作也会随着for的结束而结束 如果您在for设置的setTimeout()的timeout参数为0ms,这一位置一旦for任务结束,这些setTimeout()中指定的任务也一并安排好,for执行结束后,就会立刻执行任务队列中的其余任务(通过setTimeout()所安排的),任务插入完成后,对应的setTimeout()中的timeout参数的时间也就没被利用完了,我们仍然以timeout均为0ms为例,在本例中这些任务(打印)都是十分快速就能完成的,因此表现出来的现象就是会有5个5几乎被同时打印出来(当然,是在for结束之后才开始输出的)(如果您使用vscode执行就可以看到)(浏览器的话会给出一个5*5的简化输出)*/
代码实验结果
❯ node "d:\repos\web\Web\04-JavaScript_basic\var_settimeout.js" 0 (var:output by single console.log() after the setTimeout()) 1 (var:output by single console.log() after the setTimeout()) 2 (var:output by single console.log() after the setTimeout()) 3 (var:output by single console.log() after the setTimeout()) 4 (var:output by single console.log() after the setTimeout()) now i===4,the timestamp: 1633488870677 --------------------------------------------------------- 0 (let:output by single console.log() after the setTimeout()) 1 (let:output by single console.log() after the setTimeout()) 2 (let:output by single console.log() after the setTimeout()) 3 (let:output by single console.log() after the setTimeout()) 4 (let:output by single console.log() after the setTimeout()) the last statement(in outer context(relative to setTimeout() task queue)):over time stamp :1633488870684 for1:test insert to the task queue timeout==0 for1:test insert to the task queue timeout==0 for1:test insert to the task queue timeout==0 for1:test insert to the task queue timeout==0 for1:test insert to the task queue timeout==0 the outer setTimeout(),test first to the task queue by setTimeout():timeout=0ms for2:test insert to the task queue0 timeout==0 for2:test insert to the task queue1 timeout==0 for2:test insert to the task queue2 timeout==0 for2:test insert to the task queue3 timeout==0 for2:test insert to the task queue4 timeout==0 5 by setTimeout_handler timestamp=1633488872659 5 by setTimeout_handler timestamp=1633488872679 5 by setTimeout_handler timestamp=1633488872680 5 by setTimeout_handler timestamp=1633488872682 5 by setTimeout_handler timestamp=1633488872683 seperate two for_setTimeout():timeout=2s 0 2 4 6 8 end for_setTimeout():timeout=2.1s
结论?
中断和异步
任务队列的变化可能和中断机制有关
中断的产生/中断的执行?
首先应该理解的是,任务的安排和任务的插入以及执行是不同的,在理解任务的安排时,不用理会setTimeout()的timeout参数(也不用理会传给setTimeout()的任务函数(称之handler),
timeout体现在延迟将任务插入到队列(setTimeout将handler安排完后,就开始对timeout进行倒计时,计时结束就插入任务队列,但是,直到现在,handler要干什么都还不清楚(或者说会不会用到循环变量for里面的变量i)),
此外,尽管handler进入任务队列后也不能够确认它会立即执行,
如果它之前还有任务,它就得再等,这个时间是不定的.(根据它前面的任务的耗时)
(本例中for之类不是由计时器函数安排的任务似乎不同于由计时器函数而产生的队列任务,他们有优先执行的现象,从我设计的打印日志来看,是这样的)
the last statement(in outer context(relative to setTimeout() task queue)):over time stamp :1633488870684
这个输出出现在其他handler产生的输出之前.
handler只好等这些语句结束后再执行),任务进入执行队列后就会一个个被执行掉
而setTimeout()通过timeout参数设置延时(插入任务执行队列中)
这里for循环会执行5次,而setTimeout任务的安排(而不是插入队列,插入还要看timeout值)操作也会随着for的结束而结束
如果您在for设置的setTimeout()的timeout参数为0ms,这一位置一旦for任务结束,这些setTimeout()中指定的任务也一并安排好,for执行结束后,就会立刻执行任务队列中的任务(通过setTimeout()所安排的),对应的timeout时间到了,任务就插入完成,对应的setTimeout()中的timeout参数的时间也就没被利用完了,
我们仍然以timeout均为0ms为例,在本例中这些任务(打印)都是十分快速就能完成的,因此表现出来的现象就是会有5个5几乎被同时打印出来(当然,是在for结束之后才开始输出的)(如果您使用vscode执行就可以看到)(浏览器的话会给出一个55的简化输出)/
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 全程不用写代码,我用AI程序员写了一个飞机大战
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· 记一次.NET内存居高不下排查解决与启示
· 白话解读 Dapr 1.15:你的「微服务管家」又秀新绝活了
· DeepSeek 开源周回顾「GitHub 热点速览」