期约进度通知的实现

73 阅读2分钟

期约进度通知的实现

class TrackablePromise extends Promise {
  //executor是一个函数
  constructor(executor) {
    const notifyHandlers = [];
    super((resolve, reject) => {
      const notify = (status) => {
        notifyHandlers.map((handler) => {
          console.log("运行处理信息的函数");
          handler(status);
        });
      };
      return executor(resolve, reject, notify);
    });
    this.notifyHandlers = notifyHandlers;
  }
  notify(notifyHandler) {
    this.notifyHandlers.push(notifyHandler);
    return this;
  }
}
let p = new TrackablePromise((resolve, reject, notify) => {
  function countdown(x) {
    if (x > 0) {
      notify(`${20 * x}% remaining`);
      setTimeout(() => countdown(x - 1), 500);
    } else {
      resolve();
    }
  }
  countdown(5);
});
p.notify((msg) => console.log(`第一个信息处理函数:` + msg));
p.then(() => setTimeout(console.log, 0, "completed"));

TrackablePromise继承了Promise,并添加了新的功能,能够对Promise中的异步任务进行期约内部的进度通知。 具体的做法是:

  1. 创造TrackablePromise实例,Super传入两个Promise构造参数,resolve及reject,在原始的promise构造参数中声明notify函数。将其与原始的resolve,reject参数传入executor中,并返回调用executor产生的返回值,即TrackablePromise的实例。

其中:notify函数将notifyHandlers列表中的每个通知的具体行为进行进一步处理,将传给notify的参数进一步传入到内部的具体行为中,后续在异步代码中通过notify()调用该函数。

  1. 在实例中声明notifyHandlers列表,初始值为空列表。内部存储的是异步任务在处理过程中有关进度通知的具体行为,如:打印目前所运行的进度信息。
  2. 在异步任务的运行中,如需通知进度,则在合适的位置加入notify。

上面的代码功能已总结完毕,由于上部分代码中较为复杂,分析一下语法。

  • 由于resolve, reject经常接触,需要在Promise实例中传入(resolve,reject)=>{},这部分极为熟悉,但是当需要传入第三个notify参数时就需要在super中进行修改。super接收的是TrackablePromise的实例构造函数,在super中声明notify即可,而后送入至构造TrackablePromise函数中。
  • notify返回this,可以实现链式调用。
  • 调用 super()会调用父类构造函数,并将返回的实例赋值给 this,对理解 return executor(resolve, reject, notify);有所帮助

在计数的过程中加入期约进度通知

let p = new TrackablePromise((resolve, reject, notify) => {
  let number = 0;
  let total = 5;
  // 方法1
  // const id = setInterval(() => {
  //   if (number > total) {
  //     clearInterval(id);
  //     resolve();
  //   }
  //   number = number + 1;
  //   notify(number);
  // }, 1000);
  // 利用闭包
  for (let i = 0; i < 10; i++) {
    (function (i) {
      setTimeout(() => {
        notify(i);
      }, 1000);
    })(i);
  }
});
p.notify((msg) => console.log(msg));
p.then(() => setTimeout(console.log, 0, "completed"));