基本使用

事件循环Event Loop的机制介绍

https://jakearchibald.com/2015/tasks-microtasks-queues-and-schedules/

  1. 代码执行首先会进入一个宏任务。因为script的执行就是一个宏任务,会将script任务的指向放入调用栈主线程,然后查找同步任务进行执行。
  2. 同步任务执行完毕,查找当前宏任务中的微任务如promise,将微任务放入调用栈进行执行。执行完同步任务和微任务,本次宏任务才算执行完毕,将当前宏任务的执行出栈。
  3. 两次宏任务之间,有可能进行UI渲染更新。
  4. 开始检查下次的宏任务,即异步任务。然后再次按照宏任务内部的指向顺序依次执行。

eventLoop.png

Promise的三种状态

  • pending
  • resolved
  • rejected

状态的变化只能是由pending=>resolved或者pending=>rejected

  1. const p1 = new Promise((resolve, reject) => {});
  2. console.log("p1", p1); // <pending>
  3. const p2 = new Promise((resolve, reject) => {
  4. setTimeout(() => {
  5. resolve();
  6. }, 0);
  7. });
  8. console.log("p2", p2); //<pending>
  9. setTimeout(() => console.log("p2", p2), 10); // <fulfilled>
  10. const p3 = new Promise((resolve, reject) => {
  11. setTimeout(() => {
  12. resolve();
  13. }, 0);
  14. });
  15. console.log("p3", p3); //<pending>
  16. setTimeout(() => console.log("p3", p3), 10); // <rejected>
  • pending状态不会触发then或catch
  • resolved会触发后续then的回调函数执行
  • rejected会触发后续catch的回调函数执行 ```javascript const p4 = Promise.resolve(100); console.log(“p4”, p4); // : 100 p4.then((data) => { console.log(data); // 100 }).catch((err) => { console.log(“err”, err); });

const p5 = Promise.reject(“reject err”); console.log(“p5”, p5); // : “reject err” p5.then((data) => { console.log(data); }).catch((err) => { console.log(“err”, err); // reject err });

  1. <a name="ixMG4"></a>
  2. ### then和catch状态改变
  3. - then正常返回resolved,里面有Error则返回rejected
  4. - catch正常返回resolved,里面有Error则返回rejected
  5. ```javascript
  6. const p1= Promise.resolve(100).then(()=>{
  7. return 100
  8. })
  9. p1.then((data)=>{
  10. console.log('data',data) //没有Error,是resolved,触发then回调
  11. }).catch(err=>{
  12. console.error('err', err);
  13. })
  14. const p2= Promise.resolve(100).then(()=>{
  15. throw new Error('then error')
  16. })
  17. p2.then((data)=>{
  18. console.log('data',data)
  19. }).catch(err=>{
  20. console.error('p2-catch-err', err);// 有Error,是rejected,触发catch回调
  21. })
  22. const p3 = Promise.reject("reject-error").catch(err=>{
  23. console.error("p3 catch-err",err)
  24. })
  25. p3.then(()=>{
  26. console.log('p3') //没有Error,是resolved,触发then回调
  27. }).catch(err=>{
  28. console.error("p3-err",err)
  29. })
  30. const p4 = Promise.reject("reject-error").catch(()=>{
  31. throw new Error('p4 catch error')
  32. })
  33. p4.then((data)=>{
  34. console.log('p4',data)
  35. }).catch(err=>{
  36. console.error("p4-err",err) // 有Error,是rejected,触发catch回调
  37. })

相关面试题

  1. Promise.resolve().then(()=>{
  2. console.log(1) //1
  3. }).catch(()=>{
  4. console.log(2)
  5. }).then(()=>{
  6. console.log(3) //3
  7. })
  8. // 打印输出1、 3
  1. Promise.resolve().then(()=>{
  2. console.log(1) // 1
  3. throw new Error("message: error")
  4. }).catch(()=>{
  5. console.log(2) //2
  6. }).then(()=>{
  7. console.log(3) //3
  8. })
  9. //打印输出1、 2 、3
  1. Promise.resolve().then(()=>{
  2. console.log(1) //1
  3. throw new Error("message: error")
  4. }).catch(()=>{ // 这里没有报错,返回的就是resolved
  5. console.log(2) //2
  6. }).catch(()=>{
  7. console.log(3)
  8. })
  9. // 打印输出1、 2

使用promise,实现红3s、绿1s、黄灯2s依次交替亮灯。

  1. let red=()=>{
  2. console.log("red light")
  3. }
  4. let green=()=>{
  5. console.log("green light")
  6. }
  7. let yellow=()=>{
  8. console.log("yellow light")
  9. }
  10. // 一开始亮绿灯,绿灯3秒后,亮黄灯1s,然后亮红灯3秒,接着绿灯
  11. let run = ()=>{
  12. new Promise((res,rej)=>{
  13. setTimeout(()=>{
  14. res(yellow())
  15. },3000)
  16. }).then(()=>{
  17. setTimeout(()=>{
  18. red()
  19. },1000)
  20. }).then((res,rej)=>{
  21. setTimeout(()=>{
  22. green()
  23. },5000)
  24. })
  25. }
  26. green();
  27. run();
  28. setInterval(()=>{
  29. run()
  30. },9000)

线上完成代码:https://codepen.io/shenshuai/pen/XWRMaNN

源码分析

1处理传入的参数必须是函数,不是函数报错

  1. class NPromise{
  2. constructor(handler){
  3. // 判断传入的类型,必须是函数,否则报错
  4. if(typeof handler !== "function"){
  5. throw new TypeError("handler must be a function")
  6. }
  7. handler(this._resolve, this._reject)
  8. }
  9. _resolve(){
  10. console.log("object");
  11. }
  12. _reject(){
  13. console.log("_reject");
  14. }
  15. }

2promise 的三个状态pending/resolve/reject

初始化状态为pending,状态的转化只能从pending到resolve,或者pending到reject。状态转换后,就不能再转化。使用status标记状态转化

  1. class NPromise{
  2. static PENDING = "PENDING";
  3. static RESOLVED = "RESOLVED";
  4. static REJECTED = "REJECTED";
  5. constructor(handler){
  6. if(typeof handler !== "function"){
  7. throw new TypeError("handler must be a function")
  8. }
  9. handler(this._resolve.bind(this), this._reject.bind(this))
  10. this.status = NPromise.PENDING;
  11. }
  12. _resolve(){
  13. // 避免已经执行了reject,再次执行resolve,当状态不为pending,则退出执行
  14. if(this.status !== NPromise.PENDING) return;
  15. this.status = NPromise.RESOLVED;
  16. console.log("object", this);
  17. }
  18. _reject(){
  19. // 避免已经执行了resolve,再次执行reject,当状态不为pending,则退出执行
  20. if(this.status !== NPromise.PENDING) return;
  21. this.status = NPromise.REJECTED;
  22. console.log("_reject", this.status);
  23. }
  24. }

3then执行事件

通过设置事件队列,目的将then中的事件依次执行;

  • this.resolveQueue = [];
  • this.rejectQueue = [];

    1. class NPromise{
    2. static PENDING = "PENDING";
    3. static RESOLVED = "RESOLVED";
    4. static REJECTED = "REJECTED";
    5. constructor(handler){
    6. if(typeof handler !== "function"){
    7. throw new TypeError("handler must be a function")
    8. }
    9. this.status = NPromise.PENDING;
    10. this.resolveQueue = [];
    11. this.rejectQueue = [];
    12. handler(this._resolve.bind(this), this._reject.bind(this))
    13. }
    14. _resolve(value){
    15. // 避免已经执行了reject,再次执行resolve,当状态不为pending,则退出执行
    16. if(this.status !== NPromise.PENDING) return;
    17. this.status = NPromise.RESOLVED;
    18. let handler;
    19. //依次取出队列中的事件任务,队列的任务执行,先进入先执行,所以使用shift从头获取
    20. while(handler = this.resolveQueue.shift()){
    21. handler(value)
    22. }
    23. }
    24. _reject(value){
    25. // 避免已经执行了resolve,再次执行reject,当状态不为pending,则退出执行
    26. if(this.status !== NPromise.PENDING) return;
    27. this.status = NPromise.REJECTED;
    28. let handler;
    29. while(handler = this.rejectQueue.shift()){
    30. handler(value)
    31. }
    32. }
    33. then(resHandler, rejHandler) {
    34. this.resolveQueue.push(resHandler);
    35. this.rejectQueue.push(rejHandler);
    36. }
    37. }

    4promise的resolve和reject执行时机,微任务执行

    如果promise的resolve或reject不在异步中包含,则执行顺序会出现问题。所以要用异步微任务处理,浏览器用postMessage,node用nextTick;

    1. _resolve(value){
    2. // 避免已经执行了reject,再次执行resolve,当状态不为pending,则退出执行
    3. if(this.status !== NPromise.PENDING) return;
    4. this.status = NPromise.RESOLVED;
    5. let handler;
    6. // 浏览器环境
    7. if(typeof window !== "undefined"){
    8. window.addEventListener("message",()=>{
    9. while(handler = this.resolveQueue.shift()){
    10. handler(value)
    11. }
    12. })
    13. window.postMessage("message")
    14. }else{
    15. process.nextTick(() => {
    16. while(handler = this.resolveQueue.shift()){
    17. handler(value)
    18. }
    19. })
    20. }
    21. }
    22. _reject(value){
    23. // 避免已经执行了resolve,再次执行reject,当状态不为pending,则退出执行
    24. if(this.status !== NPromise.PENDING) return;
    25. this.status = NPromise.REJECTED;
    26. let handler;
    27. if(typeof window !== "undefined"){
    28. window.addEventListener("message",()=>{
    29. while(handler = this.rejectQueue.shift()){
    30. handler(value)
    31. }
    32. })
    33. window.postMessage("message")
    34. }else{
    35. process.nextTick(() => {
    36. while(handler = this.rejectQueue.shift()){
    37. handler(value)
    38. }
    39. })
    40. }
    41. }

    5then返回值

    返回值是promise,才可以链式调用

    1. then(resHandler, rejHandler) {
    2. // this.resolveQueue.push(resHandler);
    3. // this.rejectQueue.push(rejHandler);
    4. return new NPromise((resolve, reject) => {
    5. function handlerResolvedEvent(value) {
    6. let resResult = resHandler(value);
    7. if(resResult instanceof NPromise){
    8. // 这里是promise,需要用then调用函数
    9. resResult.then(resolve, reject);
    10. }else{
    11. resolve(value)
    12. }
    13. }
    14. function handlerRejectedEvent(value) {
    15. let rejResult = rejHandler(value);
    16. if(rejResult instanceof NPromise){
    17. // 这里是promise,需要用then调用函数
    18. rejResult.then(resolve, reject);
    19. }else{
    20. reject(value);
    21. }
    22. }
    23. this.resolveQueue.push(handlerResolvedEvent);
    24. this.rejectQueue.push(handlerRejectedEvent);
    25. });
    26. }

    6catch/finally静态方法

    catch方法是then的reject的另一种写法,对then方法需要进行改造,判断第一个参数是否存在,如果不存在则直接执行后续内容。

    1. then(resHandler, rejHandler) {
    2. // this.resolveQueue.push(resHandler);
    3. // this.rejectQueue.push(rejHandler);
    4. return new NPromise((resolve, reject) => {
    5. function handlerResolvedEvent(value) {
    6. // 判断resHandler是否是函数,即是否存在,对catch进行兼容
    7. if(typeof resHandler === 'function'){
    8. let resResult = resHandler(value);
    9. if(resResult instanceof NPromise){
    10. // 这里是promise,需要用then调用函数
    11. resResult.then(resolve, reject);
    12. }else{
    13. resolve(resResult)
    14. }
    15. }else{
    16. resolve(value)
    17. }
    18. }
    19. function handlerRejectedEvent(value) {
    20. if(typeof rejHandler === 'function'){
    21. let rejResult = rejHandler(value);
    22. if(rejResult instanceof NPromise){
    23. // 这里是promise,需要用then调用函数
    24. rejResult.then(resolve, reject);
    25. }else{
    26. reject(rejResult);
    27. }
    28. }else{
    29. reject(value);
    30. }
    31. }
    32. this.resolveQueue.push(handlerResolvedEvent);
    33. this.rejectQueue.push(handlerRejectedEvent);
    34. });
    35. }
    36. catch(reject){
    37. return this.then(undefined, reject);
    38. }

    finally方法执行,需要独立设置一个finallyQueue队列,在resolve或reject中都执行

    1. constructor(handler){
    2. if(typeof handler !== "function"){
    3. throw new TypeError("handler must be a function")
    4. }
    5. this.status = NPromise.PENDING;
    6. this.resolveQueue = [];
    7. this.rejectQueue = [];
    8. this.finallyQueue = [];
    9. handler(this._resolve.bind(this), this._reject.bind(this))
    10. }
    11. _resolve(value){
    12. // 避免已经执行了reject,再次执行resolve,当状态不为pending,则退出执行
    13. if(this.status !== NPromise.PENDING) return;
    14. this.status = NPromise.RESOLVED;
    15. console.log("_resolve object", this);
    16. let handler;
    17. // 浏览器环境
    18. if(typeof window !== "undefined"){
    19. window.addEventListener("message",()=>{
    20. while(handler = this.resolveQueue.shift()){
    21. handler(value)
    22. }
    23. })
    24. window.postMessage("message")
    25. }else{
    26. process.nextTick(() => {
    27. while(handler = this.resolveQueue.shift()){
    28. handler(value)
    29. }
    30. })
    31. }
    32. this._finally(value)
    33. }
    34. _reject(value){
    35. // 避免已经执行了resolve,再次执行reject,当状态不为pending,则退出执行
    36. if(this.status !== NPromise.PENDING) return;
    37. this.status = NPromise.REJECTED;
    38. let handler;
    39. if(typeof window !== "undefined"){
    40. window.addEventListener("message",()=>{
    41. while(handler = this.rejectQueue.shift()){
    42. handler(value)
    43. }
    44. })
    45. window.postMessage("message")
    46. }else{
    47. process.nextTick(() => {
    48. while(handler = this.rejectQueue.shift()){
    49. handler(value)
    50. }
    51. })
    52. }
    53. this._finally(value)
    54. }
    55. _finally(value){
    56. let handler;
    57. if(typeof window !== "undefined"){
    58. window.addEventListener("message",()=>{
    59. while(handler = this.finallyQueue.shift()){
    60. handler(value)
    61. }
    62. })
    63. window.postMessage("message")
    64. }else{
    65. process.nextTick(() => {
    66. while(handler = this.finallyQueue.shift()){
    67. handler(value)
    68. }
    69. })
    70. }
    71. }
    72. finally(event){
    73. this.finallyQueue.push(event);
    74. }

    7其他静态方法处理
    静态方法,resolve、reject、all、race、allSetlled

    1. static resolve(value){
    2. return new NPromise((resolve, reject)=>{
    3. resolve(value);
    4. })
    5. }
    6. static reject(value){
    7. return new NPromise((resolve, reject)=>{
    8. reject(value);
    9. })
    10. }
    11. static all(iterator){
    12. let len = iterator.length;
    13. let i = 0;
    14. let runTasks = [];
    15. return new NPromise((resolve, reject) =>{
    16. iterator.forEach(task => {
    17. task.then((res,rej)=>{
    18. i++;
    19. runTasks.push(res);
    20. if(i === len){
    21. resolve(runTasks);
    22. }
    23. }).catch(err=>{
    24. reject(err)
    25. })
    26. });
    27. })
    28. }
    29. static race(iterator){
    30. return new NPromise((resolve, reject) =>{
    31. iterator.forEach(task =>{
    32. task.then(res=>{
    33. resolve(res)
    34. }).catch(err=>{
    35. reject(err)
    36. })
    37. })
    38. })
    39. }
    40. static allSetlled(iterator){
    41. let len = iterator.length;
    42. let i = 0;
    43. let runTasks = [];
    44. return new NPromise((resolve, reject) =>{
    45. iterator.forEach(task=>{
    46. task.then(res=>{
    47. i++;
    48. runTasks.push(res);
    49. if(i===len){
    50. resolve(runTasks)
    51. }
    52. }).catch(err=>{
    53. i++;
    54. runTasks.push(err);
    55. if(i===len){
    56. resolve(runTasks)
    57. }
    58. })
    59. })
    60. })
    61. }

    完整代码地址:https://codepen.io/shenshuai/pen/jOmBwgo