事件循环
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
