Promise炖锅之微任务

274 阅读4分钟

前言

阅读本文前请先了解Promise\color{#33CC99}{Promise}基本用法

20210412-122212(WeLinkPC).png

Promise基本用法

Promise炖锅之API篇

宏任务与微任务

(大神的文章太多,这里就不展开讲了)

哪些API是微任务?

thencatchfinally

什么时候加入微任务队列?

当Promise转态为非pending时,api的回调加入微任务队列

  new Promise((resolve,reject) => {
    resolve()
  }).then(() => {
    console.log("then")
  })

上述例子中,then的回调() => { console.log("then") }会被放到微任务中

实例分析

炼气

傻瓜例子

  • point 宏任务与微任务执行先后
  • 代码
Promise.resolve()
  .then(() => {
    console.log("then");
    Promise.resolve().then(() => {
      console.log("then-1");
    });
  })
console.log("out")
  • 结果
out
then
then-1
  • 分析
  1. 代码执行到Promise.resolve().then
  2. 前者Promise已经resolved\color{#33CC99}{resolved}状态,所以then的回调放入微任务中
微任务队列压入代码:
() => {
    console.log("then");
    Promise.resolve().then(() => {
      console.log("then-1");
    });
}

3. 继续执行console.log("out"),输出out 4. 查看微任务,执行,输出then;继续执行到Promise.resolve().then 5. Promise已经resolved\color{#33CC99}{resolved}状态,then的回调放入微任务中

微任务队列压入代码:
() => {
      console.log("then-1");
}

6. 查看微任务,此时只有一个微任务,执行,输出then-1

笨蛋例子

  • point 在then回调中执行Promise.resolve(),相当于该then所返回的promise状态已经resolved
  • 代码
Promise.resolve()
  .then(() => {
    console.log("then1");
    Promise.resolve().then(() => {
      console.log("then1-1");
    });
  })
  .then(() => {
    console.log("then2");
  });
console.log("out")
  • 结果
out
then1
then1-1
then2
  • 分析
  1. 执行到第一个Promise.resolve().then,其回调压入微任务
微任务队列压入代码:
() => {
    console.log("then1");
    Promise.resolve().then(() => {
      console.log("then1-1");
    });
}

2. 继续看,第二个then由于上一个Promise状态还未定,所以其回调() => { console.log("then2");} 不会压入微任务中\color{#33CC99}{不会压入微任务中} 3. 执行console.log("out"),输出out 4. 执行微任务,输出then1;执行Promise.resolve().then,它的回调() => { console.log("then1-1"); }压入微任务中,此时第一个then的状态也变为了resolved,所以第二个then的回调也压入微任务中

微任务队列压入代码:
[
// 微任务1
() => {
      console.log("then1-1");
},

// 微任务2
() => {
    console.log("then2");
}
]

5. 执行微任务,输出then1-1,then2

筑基

开窍例子

  • point 在then回调中,return一个非promise
  • 代码
Promise.resolve()
  .then(() => {
    console.log(1);
    Promise.resolve()
      .then(() => {
        console.log(2);
        return 3;
      })
      .then((data) => {
        console.log(data);
      });
  })
  .then(() => {
    console.log(4);
  })
  .then(() => {
    console.log(5);
  })
  .then(() => {
    console.log(6);
  });
  • 结果
1
2
4
3
5
6
  • 分析
  1. 输出1,这时候内部的Promise.resolve()改变了第一个then的状态,所以将下列代码压入了微服务
微任务队列压入代码:
[
() => {
        console.log(2);
        return 3;
},
() => {
    console.log(4);
}
]

2. 执行微服务1,输出2,返回3,将后续的then回调压入微服务队列

微任务队列压入代码:
(data) => {
        console.log(data);
}

3. 执行微服务2,输出4,将后续的then回调压入微服务队列

微任务队列压入代码:
[
(data) => {
        console.log(data);
}
() => {
    console.log(5);
}
]

4. 执行微任务1,入参为3,输出3;执行微服务2,输出5,将后续回调压入微任务,随后执行输出6

掉级例子

  • point 在then回调中,return一个promise.resolve()
  • 代码
Promise.resolve()
  .then(() => {
    console.log(1);
    Promise.resolve()
      .then(() => {
        console.log(2);
        return Promise.resolve(3);
      })
      .then((data) => {
        console.log(data);
      });
  })
  .then(() => {
    console.log(4);
  })
  .then(() => {
    console.log(5);
  })
  .then(() => {
    console.log(6);
  })
  .then(() => {
    console.log(7);
  });
  • 输出
1
2
4
5
6
3
7
  • 分析
  1. 你品,你细品
  2. 简单来说,then里面返回了resolve的话,那接下来的then会落后两个微任务队列,即这里的console.log(5);console.log(6);,然后才压入微任务队列

复活例子

  • point 在then回调中,new Promise并resolve,return一个promise.resolve()

  • 代码

new Promise((resolve, reject) => {
  console.log("1");
  resolve();
})
.then(() => {
    console.log("2");
    new Promise((resolve, reject) => {
        console.log("3");
        resolve();
    })
    .then(() => {
        console.log("4");
    })
    .then(() => {
        console.log("5");
    });
    return new Promise((resolve, reject) => {
        console.log("6");
        resolve();
    })
    .then(() => {
        console.log("7");
    })
    .then(() => {
        console.log("8");
    });
})
.then(() => {
    console.log("9");
});
  • 输出
1
2
3
6
4
7
5
8
9
  • 分析
  1. 输出1,2
  2. 输出3后,第一个then回调里:内部第一个Promise状态变为resolved,注册 console.log("4")微任务;此时该宏任务未结束,继续执行到return,new了内部第二个Promise,执行同步代码console.log("6"),将该Promise状态改为resolved,将后续console.log("7")注册到微任务队列;注意,此时第一个then的状态未改变,所以不会注册第二个then回调console.log("9")到微任务队列里
  3. 执行微任务,输出4,7,(中间省略分析)5,8,这时第一个return返回的Promise状态变为resolved,注册第二个then回调console.log("9")到微任务队列里,执行

金丹

等我追追星再补上,我怕是练不成金丹了....

升级例子

  • point promise与async结合
  • 代码
async function async () {
  console.log('async start');
  await new Promise(resolve => {
    console.log('promise')
  })
  console.log('async success');
  return 'async end'
}
async().then(res => console.log(res))
  • 输出
async start
promise
  • 分析 这里需要注意的是,由于await后面的promise状态并未改变,一直都是pending,所以后续代码不会执行!只会等待!

泥淖风波例子

  • 理解then中的return
  • 代码
new Promise(resolve => {
  resolve();
}).then(() => {
  setTimeout(() => {
    console.log(1);
    Promise.resolve();
  }, 4000)
}).then(() => {
  setTimeout(() => {
    console.log(2);
  }, 1000)
})
  • 输出
2
1
  • 分析 看到没有,是2 1不是1 2!原因就在于第一个then中,虽然是延时4s才resolve,但是它没有return!!!在then中没有return或者return一个非promise,该then状态自动变为resolved!

爱谁谁

  • 理解同级then
  • 代码
setTimeout(() => {
  console.log('0');
}, 0)

new Promise((resolve, reject) => {
  console.log('1');
  resolve();
}).then(() => {
  console.log('2');
  new Promise((resolve, reject) => {
    console.log('3');
    resolve();
  }).then(() => { // 📌
    console.log('4');
  }).then(() => {
    console.log('5');
  })
}).then(() => {
  console.log('6'); // 📌
})


new Promise((resolve, reject) => {
  console.log('7');
  resolve()
}).then(() => {
  console.log('8');
})

  • 输出
1
7
2
3
8
4
6
5
0
  • 分析 看注意📌的回调,是同级的

我也不懂

  • 理解穿透
  • 代码
Promise.resolve(1)
  .then(2)
  .then(Promise.resolve(3))
  .then(data => console.log(data))
  • 分析 then 方法接受的参数是函数,而如果传递的并非是一个函数,它实际上会将其解释为 then(null),这就会导致前一个 Promise 的结果会穿透下面
  • 结果
1

参考

Promise 你真的用明白了么?