相关文章推荐
独立的书签  ·  Docker|--E: gnupg, ...·  5 月前    · 
刚毅的红金鱼  ·  CollectionViewSource ...·  10 月前    · 

问题描述:

想要在js中用setTimeout实现这么一个功能:每隔一秒输出一个数字。我们的js代码大概是这样的:

for(var i = 0; i < 3; i++) {
	setTimeout(function () {
		console.log(i);
	}, 1000);

运行这段代码会发现,程序在1秒后输出了3个3。(不但没有每隔一秒输出,而且输出的数字还全都是3)

原因分析:

这跟js的阻塞机制有关。
js阻塞机制,跟js引擎的单线程处理方式有关,每个window一个js线程。所谓单线程,即在某个特定的时刻只有特定的代码能够被执行,并阻塞其它的代码。
由于浏览器是事件驱动的(Event driven),因此浏览器中很多行为是异步(Asynchronized)的,很容易有事件被同时或者连续触发。当异步事件发生时,会创建事件并放入执行队列中,等待当前代码执行完成之后再执行这些代码,如鼠标点击事件发生、定时器触发事件发生、XMLHttpRequest完成回调这些事件,都会被放入执行队列中等待。

因此在上述的代码中,setTimeout这个定时器需要等待for循环执行完成,而for循环执行完成了之后,i已经是3了,此时才开始执行setTimeout,因此console.log(i)会是3。

我以为的程序执行过程:
预想的程序执行流程
实际的程序执行过程:
实际的程序执行流程

解决方案:

在setTimeout外部包装一个立即执行的匿名函数,将for循环的i作为参数传入进去,使得延迟函数的回调可以将新的作用域封闭在每次迭代的内部。

for (var i = 0; i < 3; i++) {
    (function (time, data) {   // 匿名函数的形参
        setTimeout(function () {
            console.log(`这是第 ${time} 次,这是其他参数:${data}`);
        }, 1000 * time);	// 还是每秒执行一次,不是累加的
    })(i, '其他参数')   // 实参,这里把要用的参数传进去
 

作用域问题
在js中,一个{}就是一个代码块,我们在for循环中定义的i变量,全局可以使用,循环中的每一次给i赋值,都是给全局变量i赋值,每次循环都会产生一个代码块,每个代码块中的都是一个新的变量

将var改为let

for (let i = 0; i < 3; i++) {
   setTimeout(() => {
	  console.log(i);
   }, 1000 * i)
 

let的作用域是块作用域,每次js把setTimeout放到队列的同时,let定义的 i 的值也会跟随setTimeout进去队列。所以每次循环后队列里的setTimeout里的let i 的值是不一样的。而var定义的 i 作用域是全局的,当程序运行到setTimeout时获取的全局var i 的值已经变成3了。

不使用setTimeout,自己写一个“休眠”函数。

function sleep(delay) {
    var start = (new Date()).getTime();
    while((new Date()).getTime() - start < delay) {
        continue;
for(var i = 0; i < 3; i++) {
	//调用自己写的sleep函数
	sleep(1000);
	console.log(i);
                    问题描述:想要用setTimeout实现这么一个功能:每隔一秒输出一个数字。我们的代码js代码大概是这样的:for(var i = 0; i &lt; 3; i++) {	setTimeout(function () {		console.log(i);	}, 1000);};运行这段代码会发现,程序在1秒后输出了3个3。(不但没有每隔一秒输出,而且输出的数字还全都是3)原因分析:这跟js的阻塞机制有关。js阻塞机制,跟js引擎的单线程处理方式有关,每个window一个js线程。所谓单
				
在写js时候,需要连续三次调用同一个接口,首先想到的就是在for循环里面循环三次调用,但是速度过快,想要每隔1秒调用一次,但是没有Thread.sleep,查找资料以后找到解决办法(原文讲的很清楚,这里直接复制过来,文末附链接): Js阻塞机制,跟Js引擎的单线程处理方式有关,每个window一个JS线程。所谓单线程,在某个特定的时刻只有特定的代码能够被执行,并阻塞其它的代码。 由于浏览器是事件驱动的(Event driven),因此浏览器很多行为是异步(Asynchronized)的,很容易有事件被同时
转自:JavaScript实现sleep睡眠函数的几种简单方法 一.什么是sleep函数? sleep是一种函数,他的作用是使程序暂停指定的时间,起到延时的效果。javascript 好像诶呦提供 sleep工具函数,所以需要自己实现 官方介绍:sleep是一种函数,作用是延时,程序暂停若干时间,在执行时要抛出一个断异常,必须对其进行捕获并处理才可以使用这个函数。 console.l...
80% 应聘者都不及格的 JS 面试题 - 掘金共 5024 字,读完需 6 分钟,速读需 2 分钟,本文首发于知乎专栏前端周刊。写在前面,笔者在做面试官这 2 年多的时间内,面试了数百个前端工程师,惊讶的发现,超过 80% 的候选人对下面这道题的回答情况连及格都达不到。这究竟是怎样神奇的一道面试题?他考察了候选人的哪些能力…https://juejin.cn/post/6844903470466629640 function sleep(delay) { let endTime = new Date().getTime()+parseInt(delay); while (new Date().getTime() < endTime );
sleep函数的作用是让线程休眠,等到指定时间再重新唤起,起到延时的效果。由于Javascript是单线程的,没有内置的sleep函数,所以要使用一些方法来模拟。通过伪死循环来阻止代码执行 以上代码不会让线程休眠,而是通过高负荷计算使CPU无暇处理其他任务; 这样会导致sleep过程其他所有的任务都被暂停,包括DOM的渲染。 方法2和方法3如果有多个sleep只能嵌套,如: 使用async/await可以实现多个sleep不用嵌套调用...
function sleep(numberMillis) { var now = new Date(); var exitTime = now.getTime() + numberMillis; while (true) { now = new Date(); if (now.getTime() >
sleep函数作用是让线程休眠,等到指定时间在重新唤起。 方法一:这种实现方式是利用一个伪死循环阻塞主线程。因为JS是单线程的。所以通过这种方式可以实现真正意义上的sleep()。 function sleep(delay) { var start = (new Date()).getTime(); while ((new Date()).getTime() - start < de...
可以使用Promise和async/await来实现同步的for循环。首先将for循环的迭代体包装在一个函数,并在函数内使用Promise来异步执行迭代体。然后使用async/await将迭代体的Promise同步化。具体实现可以参考以下代码示例: ```javascript function syncForLoop(start, end, action) { return new Promise(async (resolve, reject) => { for (let i = start; i < end; i++) { try { await action(i); // 将迭代体的异步操作同步化 } catch (error) { reject(error); resolve(); // 使用示例 async function main() { await syncForLoop(0, 10, async (i) => { console.log(`Current value: ${i}`); await new Promise((resolve) => setTimeout(resolve, 1000)); // 模拟异步操作 console.log('Loop finished!'); main(); 在上述代码,`syncForLoop`函数是一个对for循环进行了异步包装的函数,其`action`参数是一个迭代函数,用于执行每一次循环的操作。`main`函数是一个使用示例,其`syncForLoop`被用于循环输出当前值,并模拟了一个异步操作。