为了面试新人我们公司准备了一些面试题, 几个同事都尝试做了一下, 结果大片玩家 HP-100000
覆盖的考点不多, 题目挺有意思, 建议手动执行一边玩玩.
for (var i = 0; i < 5; i++) {
setTimeout(function () {
console.log(i);
}, 1000);
}
console.log(i);
for (let i = 0; i < 5; i++) {
//注意 var 变成了 let
setTimeout(function () {
console.log(i);
}, 1000);
}
console.log(i);
setTimeout
实现了 (伪) 异步, 同时因为 let 将变量作用域进行了控制, 破坏了闭包结构, 因此会按照正常顺序输出.
关于 let 关键字 1
Use the let statement to declare a variable, the scope of which is restricted to the block in which it is declared. You can assign values to the variables when you declare them or later in your script.
A variable declared using let
cannot be used before its declaration or an error will result..
同样是 Q1 的代码
for (var i = 0; i < 5; i++) {//DO NOT MODIFY
setTimeout(function () {//DO NOT MODIFY
console.log(i);
}, 1000);
}
console.log(i); //DO NOT MODIFY
Q: 修改上述代码 (部分行不允许修改, 可以在代码间插入) , 以实现"每隔一秒输出一个数字并且顺序为 0-5"
A
setTimeout
事件队列的处理for (var i = 0; i <5 ; i++) {
(function(i){
setTimeout(function(){
console.log(i)
}, 1000*i)
})(i) //将 i 作为参数传入匿名函数, 如此破坏了闭包内跨域访问
}
setTimeout(function (){
console.log(i);
}, 5000); //强行将 5 放到 5sec 后输出
考点: 闭包, (伪) 异步, 作用域, 事件队列
window.setTimeout(function () {
console.log(2);
}, 1);
//Ouput for a long time
for (var i = 0; i < 1000; i++) {
console.log("");
}
console.log(1);
window.setTimeout(function () {
console.log(3);
}, 0);
这道题目其实是其他地方抄袭来的 2, 正好和之前考点有一定重叠因此一起放了过来:
setTimeout(function(){console.log(4)}, 0);
new Promise(function(resolve){
console.log(1)
//time consuming ops
for( var i=0 ; i<10000 ; i++ ){
i==9999 && resolve();
}
console.log(2)
}).then(function(){
console.log(5)
});
console.log(3);
setTimeOut.callback
的输出, 加入 MacroTask
末端, 2. 输出 1 3. 执行 Promise.resolve()
将输出 5 的 callback 放到 MicroTask
中 (注意这里不是 MacroTask
) MacroTask
首个任务执行完毕MicroTask
里面有没有任务, 发现有, 执行, 输出 5MacroTask
里面有没有任务, 发现有, 执行, 输出 4MicroTask
里面有没有任务, 发现没有, 可以休息了MacroTask
里面有没有任务, 发现没有, 可以睡觉了闭包什么的不想写了
macrotask
和 microtask
一个事件循环 (EventLoop) 中会有一个正在执行的任务 (Task), 而这个任务就是从 macrotask 队列中来的. 在 whatwg 规范中有 queue 就是任务队列. 当这个 macrotask 执行结束后所有可用的 microtask 将会在同一个事件循环中执行, 当这些 microtask 执行结束后还能继续添加 microtask 一直到真个 microtask 队列执行结束. 1
基本来说, 当我们想以同步的方式来处理异步任务时候就用 microtask (比如我们需要直接在某段代码后就去执行某个任务, 就像 Promise 一样) .
其他情况就直接用 macrotask.
setTimeout
setInterval
setImmediate
I/O
UI 渲染
Promise
process.nextTick
Object.observe
MutationObserver
规范: https://html.spec.whatwg.org/multipage/webappapis.html#task-queue
task queue
== macrotask queue
!= microtask queue
macrotask queue
也可以放入 microtask queue
中再来回顾下事件循环如何执行一个任务的流程
当执行栈 (call stack) 为空的时候, 开始依次执行:
上面就算是一个简单的 event-loop 执行模型
再简单点可以总结为:
promiseA.then()
的回调就是一个 taskpromiseA
是 resolved
或 rejected
: 那这个 task 就会放入当前事件循环回合的 microtask queue
promiseA
是 pending
: 这个 task 就会放入 事件循环的未来的某个 (可能下一个) 回合的 microtask queue
中setTimeout
的回调也是个 task , 它会被放入 macrotask queue
即使是 0ms 的情况microtask queue
中的 task 会在事件循环的当前回合中执行, 因此 macrotask queue
中的 task 就只能等到事件循环的下一个回合中执行了