这一节的代码位于 10_source_read/10_3_async_hook

异步并行钩子AsyncParallelHook

所有函数同时执行, 执行完成后才执行最后的回调

  1. const { AsyncParallelHook } = require("./tapable/lib/index.js");
  2. class Lesson {
  3. constructor(props) {
  4. // 创建钩子
  5. this.hooks = {
  6. arch: new AsyncParallelHook(["name"])
  7. };
  8. this.counter = 0;
  9. }
  10. tap() {
  11. // 使用tapAsync注册异步事件
  12. this.hooks.arch.tapAsync("lesson1", (name, cb) => {
  13. setTimeout(() => {
  14. console.log("lesson1", name);
  15. cb(); // cb相当于计数器, 当每个cb都执行完, 才会执行真正的回调函数
  16. }, 1000);
  17. });
  18. this.hooks.arch.tapAsync("lesson2", (data, cb) => {
  19. setTimeout(() => {
  20. console.log("lesson2", data);
  21. cb();
  22. }, 2000);
  23. });
  24. }
  25. start() {
  26. this.hooks.arch.callAsync("jay", function() {
  27. console.log('end');
  28. });
  29. }
  30. }
  31. let l = new Lesson();
  32. l.tap();
  33. l.start(); // 启动钩子

执行结果: 一秒后打印lesson1 jay, 再过一秒后打印lesson2 jay和end
image.png

源码实现:

  1. class AsyncParallelHook {
  2. constructor(args) {
  3. this.tasks = [];
  4. }
  5. tapAsync(name, task) {
  6. this.tasks.push(task);
  7. }
  8. callAsync(...args) {
  9. const finalCallback = args.pop(); // 提取出最终的回调函数
  10. let counter = 0; // 用于计数异步函数完成时的回调, 每次异步函数完成, 触发回调, 计数+1
  11. // 当所有异步函数完成, 计数等于传入的异步任务数, 执行最终回调函数
  12. let done = () => {
  13. counter++;
  14. if(counter === this.tasks.length) {
  15. finalCallback();
  16. }
  17. }
  18. this.tasks.forEach(task => {
  19. task(...args, done);
  20. })
  21. }
  22. }
  23. // 测试代码
  24. const hook = new AsyncParallelHook(["name"]);
  25. hook.tapAsync("lesson1", function(name, cb) {
  26. setTimeout(() => {
  27. console.log("lesson1", name);
  28. cb();
  29. }, 1000)
  30. });
  31. hook.tapAsync("lesson2", function(name, cb) {
  32. setTimeout(() => {
  33. console.log("lesson2", name);
  34. cb();
  35. }, 2000)
  36. });
  37. hook.callAsync("jay", function () {
  38. console.log('end');
  39. });

异步并行钩子AsyncParallelHook(Promise)

和上面才去回调判断异步执行完成不同,直接使用promise对象来实现

  1. const { AsyncParallelHook } = require("./tapable/lib/index.js");
  2. class Lesson {
  3. constructor(props) {
  4. // 创建钩子
  5. this.hooks = {
  6. arch: new AsyncParallelHook(["name"])
  7. };
  8. }
  9. tap() {
  10. // 使用tapPromise注册异步事件
  11. this.hooks.arch.tapPromise("lesson1", name => new Promise((resolve, reject) => {
  12. setTimeout(() => {
  13. console.log("lesson1", name);
  14. resolve(); // 取消cb回调, 使用resolve来确认异步执行完成
  15. }, 1000);
  16. }));
  17. this.hooks.arch.tapPromise("lesson2", data => new Promise((resolve, reject) => {
  18. setTimeout(() => {
  19. console.log("lesson2", data);
  20. resolve();
  21. }, 2000);
  22. }));
  23. }
  24. start() {
  25. this.hooks.arch.promise("jay").then(function() {
  26. console.log("end");
  27. });
  28. }
  29. }
  30. let l = new Lesson();
  31. l.tap();
  32. l.start(); // 启动钩子

执行结果同上, 一秒后打印lesson1 jay, 再过一秒后打印lesson2 jay和end
image.png

源码实现:

  1. class AsyncParallelHookPromise {
  2. constructor(args) {
  3. this.tasks = [];
  4. }
  5. tapPromise(name, task) {
  6. this.tasks.push(task);
  7. }
  8. promise(...args) {
  9. let tasks = this.tasks.map(task => task(...args));
  10. return Promise.all(tasks);
  11. }
  12. }

异步串行钩子AsyncSeriesHook

上一个异步函数执行完后, 把结果传入到下一个异步函数

  1. const { AsyncSeriesHook } = require("./tapable/lib/index.js");
  2. class Lesson {
  3. constructor(props) {
  4. // 创建钩子
  5. this.hooks = {
  6. arch: new AsyncSeriesHook(["name"])
  7. };
  8. }
  9. tap() {
  10. this.hooks.arch.tapAsync("lesson1", (name, cb) => {
  11. setTimeout(() => {
  12. console.log("lesson1", name);
  13. cb();
  14. }, 1000);
  15. });
  16. this.hooks.arch.tapAsync("lesson2", (data, cb) => {
  17. setTimeout(() => {
  18. console.log("lesson2", data);
  19. cb();
  20. }, 2000);
  21. });
  22. }
  23. start() {
  24. this.hooks.arch.callAsync("jay", function() {
  25. console.log("end");
  26. });
  27. }
  28. }
  29. let l = new Lesson();
  30. l.tap();
  31. l.start(); // 启动钩子

执行结果: 过一秒出现lesson1, 再过两秒出现lesson2
image.png

源码实现:

class AsyncSeriesHook {
  constructor(args) {
    this.tasks = [];
  }

  tapAsync(name, task) {
    this.tasks.push(task);
  }

  callAsync(...args) {
        const finalCallback = args.pop();
        let counter = 0; // 判断执行了多少个异步回调
    let next = () => {
            if(this.tasks.length === counter) return finalCallback(); // 当执行的异步回调函数和任务数相同时, 执行最后的回调函数
            let task = this.tasks[counter++];
            task(...args, next);
        }
        next();
  }
}

异步串行钩子AsyncSeriesHook(Promise)

同并行的promise写法, 串行也有对应的promise模式

const { AsyncSeriesHook } = require("./tapable/lib/index.js");

class Lesson {
  constructor(props) {
    // 创建钩子
    this.hooks = {
      arch: new AsyncSeriesHook(["name"])
    };
  }

  tap() {
    this.hooks.arch.tapPromise("lesson1", name => new Promise((resolve, reject) => {
      setTimeout(() => {
        console.log("lesson1", name);
        resolve();
      }, 1000);
    }));
    this.hooks.arch.tapPromise("lesson2", data => new Promise((resolve, reject) => {
      setTimeout(() => {
        console.log("lesson2", data);
        resolve();
      }, 2000);
    }));
  }

  start() {
    this.hooks.arch.promise("jay").then(function() {
      console.log("end");
    });
  }
}

let l = new Lesson();
l.tap();
l.start(); // 启动钩子

执行结果: 过一秒出现lesson1, 再过两秒出现lesson2
image.png

源码实现:

class AsyncSeriesHookPromise {
  constructor(args) {
    this.tasks = [];
  }

  tapPromise(name, task) {
    this.tasks.push(task);
  }

  promise(...args) {
    const [first, ...other] = this.tasks;
    return other.reduce((prev, next) => prev.then(() => next(...args)), first(...args));
  }
}

异步串行瀑布流钩子AsyncSeriesWaterfallHook

通过在前一个异步任务中的回调函数中传入结果, 把这个结果传入到下一个异步任务中去, 可以在下一个异步任务中获取结果, 回调函数参数有两个, 第一个为null, 第二个为结果时, 正常的把结果向下一个异步任务传递, 第一个参数不为null时, 表示终结此次瀑布流操作

const { AsyncSeriesWaterfallHook } = require("./tapable/lib/index.js");

class Lesson {
  constructor(props) {
    // 创建钩子
    this.hooks = {
      arch: new AsyncSeriesWaterfallHook(["name"])
    };
  }

  tap() {
    this.hooks.arch.tapAsync("lesson1", (name, cb) => {
      setTimeout(() => {
        console.log("lesson1", name);
        // 此时表示把lesson1的结果传到lesson2中
        cb(null, 'lesson1 result'); 

        // cb('error', 'error result');
        // 如果cb是如此传递信息, 则中断瀑布流
      }, 1000);
    });
    this.hooks.arch.tapAsync("lesson2", (data, cb) => {
      setTimeout(() => {
        console.log("lesson2", data);
        cb();
      }, 2000);
    });
  }

  start() {
    this.hooks.arch.callAsync("jay", function() {
      console.log('end');
    });
  }
}

let l = new Lesson();
l.tap();
l.start(); // 启动钩子

打印结果为:
image.png

源码实现:

class AsyncSeriesWaterfallHook {
  constructor(args) {
    this.tasks = [];
  }

  tapAsync(name, task) {
    this.tasks.push(task);
  }

  callAsync(...args) {
    const finalCallback = args.pop();
    let counter = 0;
    const next = (err, data) => {
      let task = this.tasks[counter];
      if (!task) return finalCallback();
      if (counter === 0) {
        task(...args, next);
      } else {
        task(data, next);
      }
      counter++;
    };
    next();
  }
}

异步串行瀑布流钩子AsyncSeriesWaterfallHook(Promise)

瀑布流的promise版

const { AsyncSeriesWaterfallHook } = require("./tapable/lib/index.js");

class Lesson {
  constructor(props) {
    // 创建钩子
    this.hooks = {
      arch: new AsyncSeriesWaterfallHook(["name"])
    };
  }

  tap() {
    this.hooks.arch.tapPromise("lesson1", name => new Promise((resolve, reject) => {
      setTimeout(() => {
        console.log("lesson1", name);
        resolve("lesson1 result"); // 返回结果
      }, 1000);
    }));
    this.hooks.arch.tapPromise("lesson2", data => new Promise((resolve, reject) => {
      setTimeout(() => {
        console.log("lesson2", data);
        resolve();
      }, 2000);
    }));
  }

  start() {
    this.hooks.arch.promise("jay").then(function() {
      console.log('end');
    });
  }
}

let l = new Lesson();
l.tap();
l.start(); // 启动钩子

执行结果:
image.png

源码实现:

class AsyncSeriesWaterfallHookPromise {
  constructor(args) {
    this.tasks = [];
  }

  tapPromise(name, task) {
    this.tasks.push(task);
  }

  promise(...args) {
    const [first, ...other] = this.tasks;
    return other.reduce((prev, next) => prev.then(data => next(data)), first(...args));
  }
}