宏微任务

向下滚动可查看阅读进度

  • 宏微任务 一轮同步代码执行完以后,先把所有微任务一次执行完,再执行下一个宏任务。再把所有微任务一次执行完,再执行下一个宏任务…

    代码类型
    普通 JS 代码同步
    Promise 里面的代码同步
    Promise.then()微任务
    Promise.catch()微任务
    Promise.finally()微任务
    async里面await前面同步
    await 那一行的代码同步
    await 后面行的代码微任务
    queueMicrotask()微任务
    setTimeout()宏任务
    setInterval()宏任务

    如果一个微任务里面嵌套一个微任务,那么他们都会被同一批执行

    const promise = new Promise((resolve, reject) => {
      console.log(1);
      console.log(2);
    });
    promise.then(() => {
      console.log(3); //没有resolve,一直在pending,所以不运行
    });
    console.log(4); // 124
    async function async1() {
      console.log("1");
      await async2();
      console.log("2");
    }
    async function async2() {
      console.log("3");
    }
    setTimeout(() => console.log("4"), 0);
    async1();
    new Promise((resolve) => {
      console.log("5");
      resolve();
    }).then(() => console.log("6"));
    console.log("7");
    // 1 → 3 → 5 → 7 → 2 → 6 → 4
    new Promise((resolve) => {
      console.log(1);
      resolve(3);
      Promise.resolve().then(() => {
        console.log(4);
      });
    }).then((num) => {
      console.log(num);
    });
    setTimeout(() => {
      console.log(6);
    });
    Promise.resolve().then(() => {
      console.log(5);
    });
    console.log(2);
    
    // 124356
    console.log('start');
    
    setTimeout(() => {
        console.log('Timeout1');
    }, 1000);
    
    Promise.resolve().then(() => {
        console.log('Promise1');
    });
    
    Promise.resolve().then(() => {
        console.log('Promise2');
        setTimeout(() => {
        Promise.resolve().then(() => {
            console.log('Promise3');
        })
        console.log('Timeout2');
        }, 0);
    });
    
    console.log('end');
    // start end Promise1 Promise2 Timeout2 Promise3 Timeout1
    
    同步:
    start
    end
    
    第一轮微任务:
    Promise1
    Promise2
    
    宏任务 Timeout2:
    先同步输出 Timeout2
    并产生微任务 Promise3
    
    然后立刻清空微任务:
    Promise3
    
    之后:
    Timeout1 -->
    function app() {
      setTimeout(() => {
        console.log("1-1");
        3;
        Promise.resolve().then(() => {
          console.log("2-1");
          4;
        });
      });
      console.log("1-2");
      1;
      Promise.resolve().then(() => {
        console.log("1-3");
        2;
        setTimeout(() => {
          console.log("3-1");
          5;
        });
      });
    }
  • this 普通函数直接调用:非严格模式 -> window,严格模式 -> undefined

    对象调用:看函数名前最后一个点.前面是谁

    const f = obj.fn;f(); 此时this不是obj,而是window或者undefined

    call,bind,apply可以绑定this。只不过bind不会立刻执行该函数。

    const newFn = fn.bind(obj);
    newFn();

    new使得this指向new出来的新对象

    箭头函数没有自己的 this,它不会根据“谁调用它”决定 this。它使用的是外层作用域的 this

    const obj = {
      name: "Navi",
    
      fn() {
        const arrow = () => {
          console.log(this.name);
        };
        arrow();
      },
    };
    
    obj.fn(); //fn的this是obj,arrow用的this是fn的this,也就是obj
    var length = 10;
    function fn() {
      console.log(this.length);
    }
    var obj = {
      length: 5,
      method: function (fn) {
        //本质上都是在执行同一个 fn 函数
        fn(); // 直接调用
        arguments[0](); // this = arguments
        fn.call(obj, 12); // this = obj
      },
    };
    obj.method(fn, 1, 2);
    
    // undefined 3 5
  • 两道题,看看就行。

    const n = 10;
    function print() {
      console.log(n);
    }
    
    function f1(fn) {
      const n = 20;
      fn();
    }
    f1(print);
    // 10
    
    function fn1() {
      console.log("fn1");
    }
    var fn2;
    
    fn1();
    fn2();
    
    fn2 = function () {
      console.log("fn2");
    };
    
    fn2();
    // fn1 Uncaught TypeError: fn2 is not a function fn2
  • 闭包和循环中的var

    function createFunctions() {
      var funcs = [];
      for (var i = 0; i < 3; i++) {
        funcs[i] = function () {
          console.log(i);
        };
      }
      return funcs;
    }
    
    var funcs = createFunctions();
    funcs[0]();
    funcs[1]();
    funcs[2]();
    //输出为333,如果改成let输出则是012
    //换成let会给每一轮循环创建一个新的i,避免闭包
    //var:函数作用域 → 多轮循环共享同一个 i
    //let:块级作用域 + for 的特殊每轮绑定机制 → 每轮循环都有独立的 i
  • setTimeout和var在异步操作里面的行为

    function test() {
      for (var i = 0; i < 3; i++) {
        setTimeout(function () {
          console.log(i);
        }, 1000);
      }
    }
    
    test();
    //for 是同步执行的
    //setTimeout(...) 本身也同步执行,作用是注册定时器并开始计时。
    //但setTimeout 的回调属于宏任务,不能打断当前同步代码。
    //当前同步代码执行完后,事件循环会先清空微任务,再执行宏任务。
    //var i 是函数作用域,三轮循环共享同一个 i。
    //循环结束时 i = 3,所以输出333 可以用let代替var
    function asyncTest() {
      var result = [];
      for (var i = 0; i < 3; i++) {
        setTimeout(function () {
          result.push(i);
        }, 1000);
      }
      setTimeout(() => console.log(result), 1100); // 延迟打印结果
    }
    asyncTest();
  • this在闭包中

    var obj = {
      count: 0,
      increment: function () {
        // 这里 increment 是 obj 调用的
        // 所以这里面的 this 指向 obj
        setTimeout(function () {
          // 但这个普通函数是 setTimeout 之后单独调用的
          // 所以这里的 this 不再是 obj,而是window
          this.count++;
          console.log(this.count);
        }, 1000);
      },
    };
    
    obj.increment(); //得NaN
    
    //更改
    var obj = {
      count: 0,
    
      increment: function () {
        setTimeout(() => {
          // 箭头函数没有自己的 this
          // 会继承 increment 里的 this,也就是 obj
          this.count++;
          console.log(this.count);
        }, 1000);
      },
    };
    
    obj.increment(); // 1
    
    //或者使用bind
    increment: function() {
      // 因为是 obj.increment()
      // 所以这里 this === obj
    
      setTimeout(function() {
        this.count++;
        console.log(this.count);
      }.bind(this), 1000);
      // 这里的 this 也是 increment 里的 this
      // 所以相当于 .bind(obj)
    }