JavaScript:事件循环机制练习题

JS是一个单线程的脚本语言。
主线程先执行同步任务,然后才去执行任务队列里的任务,如果在执行宏任务之前有微任务,那么要先执行微任务,全部执行完之后等待主线程的调用,调用完之后再去任务队列中查看是否有异步任务,这样一个循环往复的过程就是事件循环!

  • 宏任务包括setTimeout、setInterval、I/O操作、ajax、事件绑定等;
  • 微任务包括new Promise()后的then与catch函数、MutationObserver、process.nextTick等。

下面这些是我收集整理的关于事件循环的练习题,希望能帮助大家更好的理解事件循环的概念。

总结做题口诀:先同步,后异步,先微任务,后宏任务。

1、下面哪些操作是异步的?
下面哪些操作是异步的?
 
a. 将数组元素进行排序
b. 发送请求获取数据
c. 计算两个数的和
d. 读取本地文件
 
答案:b和d
2、下面的代码输出什么?
console.log('start')
 
setTimeout(() => {
  console.log('timeout')
}, 0)
 
console.log('end')
 
答案:start, end, timeout
 
解析:setTimeout函数中的第二个参数表示延迟的时间,当设置为0时,setTimeout函数会被放到任务队列的末尾,等待执行栈中所有任务执行完成后再执行setTimeout函数中的回调函数。
3、下面的代码输出什么?
console.log('start')
 
setTimeout(() => {
  console.log('timeout1')
}, 0)
 
new Promise((resolve) => {
  console.log('promise1')
  resolve()
}).then(() => {
  console.log('then1')
})
 
console.log('end')
 
答案:start, promise1, end, then1, timeout1
 
解析:Promise对象是同步执行的,所以会先输出promise1。但是.then()方法是异步执行的,会被放到任务队列中等待执行,因此end会先输出。然后在任务队列中执行.then()方法,输出then1。最后在任务队列中执行setTimeout中的回调函数,输出timeout1。
4、下面代码输出什么?
console.log('start')
 
setTimeout(() => {
  console.log('timeout1')
  Promise.resolve().then(() => console.log('then2'))
}, 0)
 
new Promise((resolve) => {
  console.log('promise1')
  resolve()
}).then(() => {
  console.log('then1')
  setTimeout(() => {
    console.log('timeout2')
  }, 0)
})
 
console.log('end')
 
答案:start, promise1, end, then1, timeout1, then2, timeout2
 
解析:同样的,Promise对象和.then()方法是同步执行的,但是回调函数中包含的Promise对象和.then()方法时异步执行的,会被放到任务队列中等待执行。因此start, promise1, end, then1会先输出。然后在任务队列中执行setTimeout中的回调函数,输出timeout1,然后将包含的Promise对象和.then()方法放到任务队列中。在任务队列中执行.then()方法,输出then2。最后在任务队列中执行第二个setTimeout中的回调函数,输出timeout2。
5、下面代码输出什么?
console.log('start'); // 1
 
setTimeout(function() {
  console.log('setTimeout'); // 4
}, 0);
 
Promise.resolve().then(function() {
  console.log('promise'); // 3
});
 
console.log('end'); // 2
 
这段代码中,我们依次执行了以下操作:
 
打印"start"
打印"end"
创建一个Promise对象,并将其添加到微任务队列中
执行Promise中的回调函数,打印"promise"
执行setTimeout中的回调函数,打印"setTimeout"
根据JavaScript事件循环机制的规则,它的执行过程如下:
 
全局上下文入栈,开始执行同步任务
打印"start"
全局上下文出栈
全局上下文入栈,开始执行同步任务
打印"end"
全局上下文出栈
全局上下文入栈,开始执行同步任务
创建Promise对象,并将其添加到微任务队列中
执行Promise对象中的回调函数,打印"promise"
全局上下文出栈
执行微任务队列中的任务,打印"setTimeout"
因此,最终输出结果应该是:
 
start
end
promise
setTimeout
6、下面代码的输出结果是什么?
console.log(1);
 
setTimeout(() => {
  console.log(2);
}, 0);
 
Promise.resolve().then(() => {
  console.log(3);
});
 
console.log(4);
 
输出结果是 1 4 3 2。
 
解析:代码执行顺序为同步任务先执行,输出1,然后将异步任务放入任务队列中。setTimeout是一个宏任务,Promise.then是一个微任务。由于Promise是微任务,会优先执行,所以先输出3。然后执行完同步任务后,会依次执行微任务。所以输出顺序为1,4,3。最后执行宏任务,输出2。
7、下面代码的输出结果是什么?
console.log("start");
 
setTimeout(() => {
  console.log("setTimeout");
}, 0);
 
Promise.resolve().then(() => {
  console.log("Promise");
});
 
console.log("end");
 
输出结果是 start end Promise setTimeout。
 
解析:代码执行顺序同第一题,先输出同步任务 start 和 end,然后将异步任务放入任务队列中。由于Promise.then是一个微任务,所以会优先执行,输出 Promise。接着执行完同步任务后,依次执行微任务中的 Promise。最后执行宏任务 setTimeout 输出结果。
8、下面代码的输出结果是什么?
setTimeout(function () {
    new Promise(function (resolve, reject) {
        console.log('异步宏任务promise');
        resolve();
    }).then(function () {
        console.log('异步微任务then')
    })
    console.log('异步宏任务');
}, 0)
new Promise(function (resolve, reject) {
    console.log('同步宏任务promise');
    resolve();
}).then(function () {
    console.log('同步微任务then')
})
console.log('同步宏任务')
9、下面代码的输出结果是什么?
setTimeout(() => {
    console.log('异步1任务time1');
    new Promise(function (resolve, reject) {
        console.log('异步1宏任务promise');
        setTimeout(() => {
            console.log('异步1任务time2');
        }, 0);
        resolve();
    }).then(function () {
        console.log('异步1微任务then')
    })
}, 0);
console.log('主线程宏任务');
setTimeout(() => {
    console.log('异步2任务time2');
 
}, 0);
new Promise(function (resolve, reject) {
    console.log('宏任务promise');
    // reject();
    resolve();
}).then(function () {
    console.log('微任务then')
}).catch(function () {
    console.log('微任务catch')
})
console.log('主线程宏任务2');
10、下面代码的输出结果是什么?
for (var i = 0; i < 10; i++) {
    setTimeout(() => {
        console.log(i)
    }, 1000)
}
输出结果:10个10
解析:因为setTimeout是异步任务,会首先执行主任务。再执行异步任务。而i是全局变量。所以等异步任务执行的时候,i一直都是10 
11、下面代码的输出结果是什么?
for (var i = 0; i < 10; i++) {
    setTimeout(() => {
        console.log(i)
    }, i * 1000);
}

10
10
10
10
10
10
10
10
10
10
12、下面代码的输出结果是什么? 
for (let i = 0; i < 10; i++) {
    setTimeout(() => {
        console.log(i)
    }, i * 1000);
}

0
1
2
3
4
5
6
7
8
9
解析:大家想想,这是为什么?
13、下面代码的输出结果是什么?
for (let i = 0; i < 10; i++) {
    setTimeout(() => {
        console.log(i)
    }, 1000);
}

结果:0-9
解析:let 声明的变量会在当前的块级作用域里面(for 循环的 body 体,也即两个花括号之间的内容区域)创建一个全局词法环境(Lexical Environment),该环境里面包括了当前 for 循环过程中的 i。

  • 8
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值