前端面试——函数执行顺序练习

例子一

这里有一个例子,涉及到函数嵌套调用、微任务和宏任务。

function secondary() {
    console.log('Inside secondary');
}

function primary() {
    console.log('Start primary');

    // 微任务:Promise
    Promise.resolve().then(function promiseCallback() {
        console.log('Promise Callback in primary');
    });

    console.log('End primary');

    // 在末尾调用secondary函数
    secondary();
}

console.log('Global start');

// 调用primary函数
primary();

console.log('Global end');

// 宏任务:setTimeout
setTimeout(function timeoutCallback() {
    console.log('Timeout Callback');
}, 0);

执行以上代码时,会按照下面的步骤进行:

  1. 打印"Global start"。
  2. 开始执行primary函数。
  3. 在primary内部,打印"Start primary"。
  4. Promise.resolve().then(…)创建了一个微任务,将在当前宏任务(即primary函数)完成后执行。
  5. 打印"End primary"。
  6. 调用并执行secondary函数,打印"Inside secondary"。
  7. primary函数执行完毕,控制权返回到全局上下文。
  8. 打印"Global end"。
  9. 当前宏任务结束,开始执行微任务队列中的任务。
    10 执行微任务,打印"Promise Callback in primary"。
  10. 微任务队列为空,事件循环移动到下一个宏任务。
  11. 执行由setTimeout(…, 0)排定的宏任务,打印"Timeout Callback"。

所以,程序的输出将会是:

Global start
Start primary
End primary
Inside secondary
Global end
Promise Callback in primary
Timeout Callback

这个例子清晰地展示了JavaScript事件循环中同步代码、微任务和宏任务的执行顺序。主要要点是,同步代码总是首先执行,微任务会在当前宏任务结束后执行,而新的宏任务会在所有微任务执行完毕后才开始执行。

例子2

function task() {
    console.log('Performing a task...');
}

function complexFunction() {
    console.log('Start of complexFunction');

    // 微任务:通过Promise.then添加的回调
    Promise.resolve().then(() => {
        console.log('Promise resolved in complexFunction');
    });

    // 宏任务:通过setTimeout添加的回调
    setTimeout(() => {
        console.log('Timeout in complexFunction');
    }, 0);

    console.log('End of complexFunction');

    // 在complexFunction结尾处调用task函数
    task();
}

console.log('Script start');

// 调用 complexFunction
complexFunction();

console.log('Script end');

// 宏任务:全局 setTimeout 回调
setTimeout(() => {
    console.log('Global timeout');
}, 0);

// 微任务:全局 Promise.then 回调
Promise.resolve().then(() => {
    console.log('Global promise resolved');
});

执行这个脚本时代码的执行顺序将会如下:

  1. 打印"Script start"。
  2. 开始执行complexFunction()
  3. 打印"Start of complexFunction"。
  4. 创建一个微任务(Promise resolved in complexFunction)。
  5. 创建一个宏任务(Timeout in complexFunction)。
  6. 打印"End of complexFunction"。
  7. 调用并且执行task()函数,打印"Performing a task…"。
  8. complexFunction()执行完毕,控制权返回到全局执行上下文。
  9. 打印"Script end"。
  10. 当前宏任务结束,JavaScript运行时开始处理微任务队列。
  11. 执行微任务,打印"Promise resolved in complexFunction"。
  12. 执行微任务,打印"Global promise resolved"。
  13. 微任务队列为空,事件循环移动到下一个宏任务。
  14. 执行由complexFunction中的setTimeout排定的宏任务,打印"Timeout in complexFunction"。
  15. 执行全局的setTimeout排定的宏任务,打印"Global timeout"。

所以,整个脚本的输出将会是:

Script start
Start of complexFunction
End of complexFunction
Performing a task...
Script end
Promise resolved in complexFunction
Global promise resolved
Timeout in complexFunction
Global timeout

这个例子提供了更详细的视角,帮助理解在复杂情况下,同步代码、微任务、宏任务之间的关系和执行顺序。

例子3

function log(message) {
    console.log(message);
}

function processItem(item) {
    log(`Processing item: ${item}`);
}

function complexOperation() {
    log('Complex operation started');

    setTimeout(() => {
        log('Timeout 1 triggered');
        Promise.resolve().then(() => log('Microtask 1 from Timeout 1'));
    }, 0);

    Promise.resolve().then(() => {
        log('Microtask 1 triggered');
        setTimeout(() => log('Timeout 2 from Microtask 1'), 0);
    });

    Promise.resolve().then(() => log('Microtask 2 triggered'));

    log('Complex operation ended');
}

function executeSequence() {
    log('Sequence started');
    complexOperation();
    log('Sequence continued');
    processItem(1);
    processItem(2);
    log('Sequence ended');
}

log('Script start');

setTimeout(() => {
    log('Global timeout triggered');
    Promise.resolve().then(() => log('Microtask from Global timeout'));
}, 0);

executeSequence();

Promise.resolve().then(() => log('Global microtask triggered'));

log('Script end');

执行这段代码时,事件将按以下顺序发生:

  1. 打印"Script start"。
  2. 创建一个全局宏任务(Global timeout triggered)。
  3. 调用executeSequence()
  4. 打印"Sequence started"。
  5. 调用complexOperation()
  6. 打印"Complex operation started"。
  7. 创建一个宏任务(Timeout 1 triggered)。
  8. 创建第一个微任务(Microtask 1 triggered)。
  9. 创建第二个微任务(Microtask 2 triggered)。
  10. 打印"Complex operation ended"。
  11. 返回到executeSequence()并打印"Sequence continued"。
  12. 调用processItem(1)并打印"Processing item: 1"。
  13. 调用processItem(2)并打印"Processing item: 2"。
  14. 打印"Sequence ended"。
  15. executeSequence()执行完毕,控制权返回至全局上下文。
  16. 打印"Script end"。
  17. 当前宏任务结束,开始处理微任务队列。
  18. 执行第一个微任务,打印"Microtask 1 triggered"并创建一个新宏任务(Timeout 2 from Microtask 1)。
  19. 执行第二个微任务,打印"Microtask 2 triggered"。
  20. 执行全局微任务,打印"Global microtask triggered"。
  21. 微任务队列清空,事件循环移动到下一个宏任务。
  22. 执行由complexOperation中的setTimeout排定的宏任务(Timeout 1 triggered),打印"Timeout 1 triggered"并创建一个新微任务(Microtask 1 from Timeout 1)。
  23. 执行由第一个微任务中的setTimeout排定的宏任务(Timeout 2 from Microtask 1),打印"Timeout 2 from Microtask 1"。
  24. 执行全局setTimeout排定的宏任务(Global timeout triggered),打印"Global timeout triggered"并创建一个新微任务(Microtask from Global timeout)。
  25. 处理微任务队列:打印"Microtask 1 from Timeout 1"。
  26. 处理微任务队列:打印"Microtask from Global timeout"。

整个脚本的输出应该如下:

Script start
Sequence started
Complex operation started
Complex operation ended
Sequence continued
Processing item: 1
Processing item: 2
Sequence ended
Script end
Microtask 1 triggered
Microtask 2 triggered
Global microtask triggered
Timeout 1 triggered
Timeout 2 from Microtask 1
Global timeout triggered
Microtask 1 from Timeout 1
Microtask from Global timeout

此示例展示了嵌套函数调用与 JavaScript 事件循环中的微任务、宏任务之间复杂的交互。每一步都是按照 JavaScript 运行时的规则来执行的,同步代码总是首先执行,接着是微任务,最后是宏任务。在宏任务中添加的微任务会在当前宏任务完成后,在下一轮事件循环中先于其他宏任务执行。

  • 24
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值