宏任务微任务案例

131 阅读5分钟
const pro = new Promise((resolve, reject) => {  
    const innerpro = new Promise((r, reject) => {  
        setTimeout(() => {  
            r(1);  
        });  
        console.log(2);  
        r(3);  
    });  
    resolve(4);  
    innerpro.then((res) => console.log(res));  
    console.log('yideng');  
})  
pro.then((res) => console.log(res));  
console.log('end');
  • 执行结果:
2
yideng
end
3
4
  • pro中的函数体打印 2 yideng与console.log('end')打印 end同步执行完后,才会执行pro.then()微任务的结果。
  • pro.then()与innerpro.then()是微任务,内部微任务会先执行打印 3,再执行外部微任务打印 4
  • setTimeout 是宏任务,执行完以上同步任务与微任务后,就会执行setTimeout结果,由于setTimeout 内容是执行 innerpro.then()的结果,而 innerpro.then()在第一次执行完后就被释放,因此没有打印 1

  • 同步任务结果:2 yiding end
  • 微任务结果:3 4
  • 宏任务结果:innerpro被释放无法再次执行

  • 要点:
  • Promise函数中resolve的位置没有决定性,只有执行完函数体中的内容后,才会执行Promise.then()的结果,Promise.then()是微任务,函数体中内容执行完后才会执行。

setTimeout(() => {  
    console.log(1);  
}, 0);  

setImmediate(() => {  
    console.log(2);  
});
  • 结果可能是1 2,也可能是2 1
  • 原因解释:
    1. setTimeout(fn, 0)和 setTimeout(fn, 1)是等价的:
      • Node.js中最小延迟时间是1ms
      • 即使设置为0,也会被强制设置为1ms
    2. setImmediate是在当前轮询阶段结束后执行
      • 会在I/O事件回调之后执行
      • 但在setTimeoutsetImmediate之前执行
    3. 执行顺序取决于进入事件循环时的时机:
      • 若进入事件循环时已经超过了1mssetTimeout会先执行
      • 若进入事件循环时还不到1mssetImmediate会先执行
  • Node.js事件循环的阶段顺序:
    1. timers(setTimeout/setInterval回调)
    2. pending callbacks
    3. idle,prepare
    4. poll(I/O回调)
    5. check(setImmediate回调)
    6. close callbacks
  • 若想保证执行顺序,最好的方式是将它们放在I/O操作会调中:
// 在I/O操作的回调中,setImmediate 总是先于 setTimeout执行
fs.readFile('file.txt', ()=>{
    setTimeout(()=>{
        console.log(1);
    }, 0);
    setImmediate(()=>{
        console.log(2);
    })
});
// 该情况下,一定是先打印 2,再打印 1
  • 因为在I/O操作回调中,代码是在poll阶段执行的,接下来一定是先进入check阶段(执行setImmediate),然后才会进入timers阶段(执行setTimeout)。

Promise.resolve()  
    .then(() => {  
        console.log(1);  
    })  
    .then(() => {  
        console.log(2);  
    })  
    .then(() => {  
        console.log(3);  
    });  

Promise.resolve()  
    .then(() => {  
        console.log(4);  
    })  
    .then(() => {  
        console.log(5);  
    })  
    .then(() => {  
        console.log(6);  
    });
  • 打印顺序是:1 4 2 5 3 6

  • 两个独立的Promise链,它们会交替执行

  • 执行过程:

    • 第一个Promise.resolve()创建一个已完成的Promise
    • 第二个Promise.resolve()创建第二个已完成的Promise
    • 两个Promise链的第一个then都被加入微任务队列
    • 执行第一个链的第一个then,打印1
    • 执行第二个链的第一个then,打印4
    • 第一个链的第二个then被加入队列
    • 第二个链的第二个then被加入队列
    • 依此类推~~~
  • 关键点:

    • 每个.then()都会返回一个新的Promise
    • 每个.then()的回调都是一个新的微任务
    • 两个Promise链会交替执行,因为它们是同时启动的
    • 每个链中的顺序是确定的(1-2-3 和 4-5-6)
  • 总结:

    • 该交替执行模式展示了Promise的并发特性,虽然都是微任务,但多个Promise链可交替执行,而不是一个链完全执行完再执行另一个链。
  • 若想改变执行顺序,让第一个链完全执行完再执行第二个链

Promise.resolve()  
    .then(() => {  
        console.log(1);  
    })  
    .then(() => {  
        console.log(2);  
    })  
    .then(() => {  
        console.log(3);  
    })  
    .then(() => {  
        Promise.resolve()  
            .then(() => {  
                console.log(4);  
            })  
            .then(() => {  
                console.log(5);  
            })  
            .then(() => {  
                console.log(6);  
            });  
    });  
// 输出:1, 2, 3, 4, 5, 6

eventLoop运行

console.log("1");

setTimeout(function () {
  console.log("2");
  process.nextTick(function () {
    console.log("3");
  });
  new Promise(function (resolve) {
    console.log("4");
    resolve();
  }).then(function () {
    console.log("5");
  });
}, 0);
process.nextTick(function () {
  console.log("6");
});
new Promise(function (resolve) {
  console.log("7");
  resolve();
}).then(function () {
  console.log("8");
});

setTimeout(function () {
  console.log("9");
  process.nextTick(function () {
    console.log("10");
  });
  new Promise(function (resolve) {
    console.log("11");
    resolve();
  }).then(function () {
    console.log("12");
  });
}, 0);

//process.nextTick 是微任务,优先级高于Promise
//node 11版本前 下 1 7 6 8 2 4 9 11 3 10 5 12  宏任务执行完后,会把当前队列中所有的微任务先执行,在执行下一轮宏任务  process.nextTick > Promise
//js  或11版本之后 1 7 6 8 2 4 3 5 9 11 10 12  会把当前宏任务中的微任务优先执行,也就是局部宏任务中的微任务全部执行完,才执行下一个宏任务

promise执行顺序

async function testSometing() {
  console.log("#testSometing");
  // 在await后面的代码会加入微任务队列,等待执行。等同于Promise.resolve("testSometing")
  return "testSometing";
}

async function testAsync() {
  console.log("执行testAsync");
  return Promise.resolve("hello async");
}

async function test() {
  console.log("test start...");

  const v1 = await testSometing();
  console.log(v1);

  const v2 = await testAsync();
  console.log(v2);

  console.log(v1, v2);
}

test();

var promise = new Promise((resolve) => {
  console.log("promise start...");
  resolve("promise");
});

promise.then((val) => console.log(val));

console.log("test end...");
// 在await后面的代码会加入微任务队列,等待执行。
// test start...
// #testSometing
// promise start...
// test end...
// testSometing
// 执行testAsync
// promise
// hello async
// testSometing hello async

promise执行顺序2

// 定义第一个异步函数 async1
async function async1() {
  console.log("async1 start"); // 打印 async1 开始
  await async2(); // 等待 async2 执行完成
  console.log("async1 end"); // 打印 async1 结束
}

// 定义第二个异步函数 async2
async function async2() {
  console.log("async2"); // 打印 async2
  // 使用 setTimeout 创建一个宏任务,延时 0ms
  setTimeout(function () {
    console.log("setTimeout"); // 打印 setTimeout
  }, 0);
}

// console.log('script start'); // 这行代码在图片中被注释掉了,可以选择取消注释
// 打印脚本开始

// 调用 async1 函数
async1();

// 创建一个新的 Promise
new Promise(function (resolve) {
  console.log("promise1"); // 打印 promise1
  resolve(); // 解析 Promise
}).then(function () {
  console.log("promise2"); // 打印 promise2
});
// async 函数会阻塞当前的同步代码,直到异步操作完成,然后返回结果。
// await 会阻塞当前的同步代码,直到异步操作完成,然后返回结果。

// async1 start
// async2
// promise1
// async1 end
// promise2
// setTimeout

test.1

console.log('1');

setTimeout(function() {
    console.log('2');
    new Promise(function(resolve) {
        console.log('4');
        resolve();
    }).then(function() {
        console.log('5')
    })
})
new Promise(function(resolve) {
    console.log('7');
    resolve();
}).then(function() {
    console.log('8')
})

setTimeout(function() {
    console.log('9');
    new Promise(function(resolve) {
        console.log('11');
        resolve();
    }).then(function() {
        console.log('12')
    })
})
//1 7 8 2 4 5 9 11 12   因为微任务包裹在宏任务里面  需要等待宏任务执行完之后在执行其他宏任务

test.2

setTimeout(function(){
  console.log(2);
},0);
new Promise(function(resolve){
  console.log(3);
  resolve();
  console.log(4);
}).then(function(){
  console.log(5);
});
console.log(6);
console.log(8);
//3 4 6 8 5 2