VuePressVuePress
vue2
vue3
React
css
javascript
实操题目
http
真题
事件循环
题目
vue2
vue3
React
css
javascript
实操题目
http
真题
事件循环
题目
  • 事件循环

事件循环

sleep 函数( 休眠函数)

名称:异步延迟(Asynchronous Delay)或非阻塞延迟(Non-blocking Delay) 行为:异步地等待指定的时间(delay 毫秒),然后继续执行后续代码。 影响:不会阻塞主线程,其他代码可以在等待期间继续执行。

const sleep = (ms = 0) => new Promise((resolve) => setTimeout(resolve, ms));

delay 函数( 死循环函数 )

名称:同步延迟(Synchronous Delay)或阻塞延迟(Blocking Delay)。 行为:使用同步机制(如 while 循环)来实现延迟,会阻塞主线程,直到延迟时间结束。 影响:会阻塞主线程,其他代码在等待期间无法执行。

const delay = (t) => {
  const start = Date.now();
  while (Date.now() - start < t) {}
};

定时器事例 1

题目

const sleep = (ms = 0) => new Promise((resolve) => setTimeout(resolve, ms));

async function main() {
  console.log("hello");
  await sleep(5000);
  console.log("world");
}
main();

结果

1. hello
2. promise <pedding>
3. 等待 5s
4. world

定时器事例 2

题目

const sleep = (ms = 0) => new Promise((resolve) => setTimeout(resolve, ms));

async function main() {
  console.info("start", Date.now());
  setTimeout(() => {
    console.log("hello", Date.now());
  }, 1000);
  await sleep(5000);
  console.log("world", Date.now());
}
main();

结果

1. start
2. promise <pedding>
3. 等待1秒 hello
3. 等待 4s
4. world

定时器事例 3

题目

const sleep = (ms = 0) => new Promise((resolve) => setTimeout(resolve, ms));

sleep(1000).then(function () {
  console.info(2);
  sleep(2000).then(function () {
    console.info(3);
  });
});

结果

promise <pedding>
等待 1s
console.info(2);
等待2s
console.info(3);

定时器事例 4

题目

for (var i = 1; i <= 5; i++) {
  setTimeout(function timer() {
    console.log(i);
  }, i * 1000);
}

结果

// 每隔1s 打印一个6
// 打印结果
6;
6;
6;
6;
6;

解释:for (var i=1; i<=5; i++) 这个循环从 i=1 开始,每次循环后 i 增加 1,直到 i 大于 5 为止, 到那时 i 已经是 6 了,   这是因为 setTimeout 是异步执行,每一次 for 循环的时候,setTimeout 都执行一次,但是里面的函数没有被执行,而是被放到了任务队列里,等待执行。只有主线上的任务执行完,才会执行任务队列里的任务。也就是说它会等到 for 循环全部运行完毕后,才会执行 fun 函数,但是当 for 循环结束后此时 i 的值已经变成了 6,因此虽然定时器跑了 5 秒,控制台上的内容依然是 6。

定时器事例 5

题目

改造一下例子 ,使用let来定义

for (let i = 1; i <= 5; i++) {
  setTimeout(function timer() {
    console.log(i);
  }, i * 1000);
}

结果

// 每隔1s
// 打印结果
1;
2;
3;
4;
5;

因为 let 有块级作用域,每次循环都会创建一个新的 i 变量:

定时器事例 6

题目

闭包方式

for (var i = 1; i <= 5; i++) {
  (function () {
    setTimeout(function timer() {
      console.log(i);
    }, i * 1000);
  })();
}

结果

// 每隔1s 打印一个6
// 打印结果
6;
6;
6;
6;
6;

定时器事例 7

题目

for (var i = 1; i <= 5; i++) {
  (function (j) {
    setTimeout(function timer() {
      console.log(j);
    }, j * 1000);
  })(i);
}

结果

// 每隔1s
// 打印结果
1;
2;
3;
4;
5;

通过闭包,将 i 的变量驻留在内存中,当输出 j 时,引用的是外部函数的变量值 i,i 的值是根据循环来的,执行 setTimeout 时已经确定了里面的的输出了。

定时器事例 8

题目

for (var i = 0; i < 5; i++) {
  (function (j) {
    setTimeout(function timer() {
      console.log(new Date(), j);
    }, 1000);
  })(i);
}

结果

// 1s后 一次性 打印结果
0;
1;
2;
3;
4;

定时器事例 9

题目

for (var i = 0; i < 5; i++) {
  (function () {
    setTimeout(function timer() {
      console.log(new Date(), i);
    }, 1000);
  })();
}

结果

// 1s后 一次性 打印结果
5;
5;
5;
5;
5;

定时器事例 10

题目

for (let i = 0; i < 5; i++) {
  (function () {
    setTimeout(function timer() {
      console.log(new Date(), i);
    }, 1000);
  })();
}

结果

// 1s后 一次性 打印结果
0;
1;
2;
3;
4;

定时器事例 11

题目

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

结果

// 一瞬间 打印结果
0;
1;
2;
3;
4;
...
9;

会立即输出 0 到 9,而不是你可能期望的延迟输出。这是因为 setTimeout 的第一个参数是一个函数引用,而不是函数调用。 当前代码实际上是在循环开始时就执行了 console.log(i),并将其返回值(undefined)传递给 setTimeout。

定时器事例 12

题目

for (var i = 0; i < 10; i++) {
  setTimeout("console.log(i)", 0);
}

结果

// 一瞬间 打印结果
10个10

setTimeout的参数

这两个 setTimeout 调用的区别在于它们传递给 setTimeout 的参数类型和执行时机

1. setTimeout(console.log(1), 1000)

在这个例子中,console.log(1) 是一个函数调用。它会立即执行,并返回 undefined。因此,这个代码实际上等同于:

setTimeout(undefined, 1000);

这意味着 setTimeout 并没有接收到一个有效的回调函数,所以不会在 1000 毫秒后执行任何操作。

2. setTimeout("console.log(1)", 1000)

在这个例子中,传递给 setTimeout 的是一个字符串 "console.log(1)"。在这种情况下,setTimeout 会使用 eval 来执行这个字符串。这意味着在 1000 毫秒后,JavaScript 引擎会执行 console.log(1)。

虽然这种用法是有效的,但通常不推荐使用字符串作为 setTimeout 的参数,因为它会引发安全性和性能问题。更好的做法是传递一个函数引用:

setTimeout(function () {
  console.log(1);
}, 1000);

或者使用箭头函数:

setTimeout(() => console.log(1), 1000);

Promise 事例 1

参考文章: 10 个关于 Promise 和 setTimeout 知识的面试题,通过图解一次说透彻

题目

console.log("Start");

const promise = new Promise((resolve, reject) => {
  console.log("Promise executor");
  resolve("Success");
});

promise.then((value) => {
  console.log(value);
});

console.log("End");

结果

1 输出 Start。
2 创建 Promise 对象,并立即执行其内部代码,输出 Promise executor。
3 调用 resolve('Success');,将 Promise 状态变为pedding--> “fulfilled”。
4 将 then 方法中的回调函数放入微任务队列。
5 输出 End。
6 当前事件循环结束后,执行微任务队列中的回调函数,输出 Success。

Promise 事例 2

题目

console.log("Start");
const promise1 = new Promise((resolve, reject) => {
  console.log(1);
});
promise1.then(() => {
  console.log(3);
});
console.log(4);

结果

由于 Promise 没有被 resolve 或 reject,then 回调函数不会执行,所以不会输出 3。

1 输出 Start。
2 创建 Promise 对象,并立即执行其内部代码,输出 Promise executor。
3 输出 1
5 输出 4

Promise 事例 3

题目

const promise1 = new Promise((resolve, reject) => {
  console.log(1);
  resolve("resolve1");
});
const promise2 = promise1.then((res) => {
  console.log(res);
});
console.log("promise1:", promise1);
console.log("promise2:", promise2);

结果

打印1
promise1: Promise {<fulfilled>: 'resolve1'}
promise2: Promise {<pending>}
resolve1

Promise 事例 3

题目

const timer1 = setTimeout(() => {
  console.log("timer1");
  const promise1 = Promise.resolve().then(() => {
    console.log("promise1");
  });
}, 0);
const timer2 = setTimeout(() => {
  console.log("timer2");
}, 0);
console.log("start");

结果

// 在执行宏任务的时候 出现微任务了, 则 立即执行 微任务
start;
timer1;
promise1;
timer2;

Promise 事例 4

const promise1 = new Promise((resolve, reject) => {
  console.log("promise1");
});
console.log("1", promise1);

结果

从上至下,先遇到 new Promise,执行该构造函数中的代码 promise1
然后执行同步代码 1,此时 promise1 没有被 resolve 或者 reject,因此状态还是 pending

'promise1'
'1' Promise{<pending>}

Promise 事例 5

const promise1 = new Promise((resolve, reject) => {
  console.log("promise1");
  resolve("resolve1");
});
const promise2 = promise1.then((res) => {
  console.log(res);
});
console.log("1", promise1);
console.log("2", promise2);

结果

- 从上至下,先遇到new Promise,执行该构造函数中的代码promise1
- 碰到resolve函数, 将promise1的状态改变为resolved, 并将结果保存下来
- 碰到promise1.then这个微任务,将它放入微任务队列
- promise2是一个新的状态为pending的Promise
- 执行同步代码1, 同时打印出promise1的状态是resolved
- 执行同步代码2,同时打印出promise2的状态是pending
- 宏任务执行完毕,查找微任务队列,发现promise1.then这个微任务且状态为resolved,执行它。

Promise 事例 5

Promise.resolve()
  .then(() => {
    console.info(0);
    return Promise.resolve(9); // 慢2拍
  })
  .then((res) => {
    console.info(res);
  });

Promise.resolve()
  .then(() => {
    console.info(1);
  })
  .then((res) => {
    console.info(2);
  })
  .then((res) => {
    console.info(3);
  })
  .then((res) => {
    console.info(4);
  });

// 0 1 2 3 9 4
Last Updated:
Contributors: zhanghusheng