Skip to content

AsyncParallelBailHook

AsyncParallelBailHook 为异步并行执行的。和我们上面的 AsyncParallelHook 一样,通过使用 tapAsync 注册事件,通过 callAsync 触发事件,也可以通过 tapPromise 注册事件,使用 promise 来触发。

其与其他的 AsyncParallelHook 不同的地方在于只要有一个在 done(undefined,true),就会执行整个的回调,但是不阻止后面事件流的执行和回调

例子

js
const { AsyncParallelBailHook } = require("tapable")

// 创建一个同步事件流实例对象
const h1 = new AsyncParallelBailHook(["xxx", "arg2"])

h1.intercept({
  call: (source, target, routesList) => {
    console.log("Starting to h1 routes")
  },
  register: tapInfo => {
    // tapInfo = { type: "promise", name: "GoogleMapsPlugin", fn: ... }
    console.log(`${tapInfo.name} is doing its job`)
    return tapInfo // may return a new tapInfo object
  },
  tap: tap => {
    console.log(tap, "444444")
  },
})

// 添加同步事件A
h1.tapAsync("A", (name, age, done) => {
  setTimeout(() => {
    console.log("A", name, age, new Date().getSeconds())
    done(undefined, true)
  }, 2000)
})

// 添加同步事件B
h1.tapAsync("B", (name, age, done) => {
  setTimeout(() => {
    console.log("B", name, age, new Date().getSeconds())
    done()
  }, 3000)
})

// 添加同步事件C
h1.tapAsync("C", (name, age, done) => {
  setTimeout(() => {
    console.log("C", name, age, new Date().getSeconds())
    done()
  }, 2000)
})
// 添加同步事件F
h1.tapAsync(
  {
    name: "F",
    before: "D",
  },
  (name, age, done) => {
    setTimeout(() => {
      console.log("F", name, age, new Date().getSeconds())
      done()
    }, 2000)
  }
)
// 添加同步事件E
h1.tapAsync(
  {
    name: "E",
    before: "C",
  },
  (name, age, done) => {
    setTimeout(() => {
      console.log("E", name, age, new Date().getSeconds())
      done()
    }, 2000)
  }
)
// 添加同步事件D
h1.tapAsync("D", (name, age, done) => {
  setTimeout(() => {
    console.log("D", name, age, new Date().getSeconds())
    done()
  }, 2000)
})

// 执行这个事件流
h1.callAsync("立早", 20, () => {
  console.log("函数执行完毕")
})

结果为

[HMR] Waiting for update signal from WDS...
A is doing its job
B is doing its job
C is doing its job
F is doing its job
E is doing its job
D is doing its job

Starting to h1 routes
 {type: "async", fn: ƒ, name: "F", before: "D"} "444444"
 {type: "async", fn: ƒ, name: "A"} "444444"
 {type: "async", fn: ƒ, name: "B"} "444444"
 {type: "async", fn: ƒ, name: "E", before: "C"} "444444"
 {type: "async", fn: ƒ, name: "C"} "444444"
 {type: "async", fn: ƒ, name: "D"} "444444"

[WDS] Hot Module Replacement enabled.

F 立早 20 30
A 立早 20 30
函数执行完毕
E 立早 20 30
C 立早 20 30
D 立早 20 30
B 立早 20 31

发现其秒数都是 30 秒(31 是延迟 3 秒),那么说明所有的事件流是并行执行的。

其中在 A 中 done(undefined , 'gzhA'); 就直接执行了整个钩子的回调 done()

原理

this.content()

js
class AsyncSeriesBailHookCodeFactory extends HookCodeFactory {
  content({ onError, onResult, onDone }) {
    return this.callTapsSeries({
      onError: (i, err, next, doneBreak) => onError(err) + doneBreak(true),
      onResult: (i, result, next) => `if(${result} !== undefined) {\n${onResult(result)};\n} else {\n${next()}}\n`,
      onDone,
    })
  }
}
全部伪代码
js
var _results = new Array(6)
var _checkDone = () => {
  for (var i = 0; i < _results.length; i++) {
    var item = _results[i]
    if (item === undefined) return false
    if (item.result !== undefined) {
      _callback(null, item.result)
      return true
    }
    if (item.error) {
      _callback(item.error)
      return true
    }
  }
  return false
}
do {
  var _counter = 6
  var _done = () => {
    _callback()
  }
  if (_counter <= 0) break
  var _tap0 = _taps[0]
  _interceptors[0].tap(_tap0)
  var _fn0 = _x[0]
  _fn0(xxx, arg2, (_err0, _result0) => {
    if (_err0) {
      if (_counter > 0) {
        if (0 < _results.length && ((_results.length = 1), (_results[0] = { error: _err0 }), _checkDone())) {
          _counter = 0
        } else {
          if (--_counter === 0) _done()
        }
      }
    } else {
      if (_counter > 0) {
        if (
          0 < _results.length &&
          (_result0 !== undefined && (_results.length = 1), (_results[0] = { result: _result0 }), _checkDone())
        ) {
          _counter = 0
        } else {
          if (--_counter === 0) _done()
        }
      }
    }
  })
  if (_counter <= 0) break
  if (1 >= _results.length) {
    if (--_counter === 0) _done()
  } else {
    var _tap1 = _taps[1]
    _interceptors[0].tap(_tap1)
    var _fn1 = _x[1]
    _fn1(xxx, arg2, (_err1, _result1) => {
      if (_err1) {
        if (_counter > 0) {
          if (1 < _results.length && ((_results.length = 2), (_results[1] = { error: _err1 }), _checkDone())) {
            _counter = 0
          } else {
            if (--_counter === 0) _done()
          }
        }
      } else {
        if (_counter > 0) {
          if (
            1 < _results.length &&
            (_result1 !== undefined && (_results.length = 2), (_results[1] = { result: _result1 }), _checkDone())
          ) {
            _counter = 0
          } else {
            if (--_counter === 0) _done()
          }
        }
      }
    })
  }
  if (_counter <= 0) break
  if (2 >= _results.length) {
    if (--_counter === 0) _done()
  } else {
    var _tap2 = _taps[2]
    _interceptors[0].tap(_tap2)
    var _fn2 = _x[2]
    _fn2(xxx, arg2, (_err2, _result2) => {
      if (_err2) {
        if (_counter > 0) {
          if (2 < _results.length && ((_results.length = 3), (_results[2] = { error: _err2 }), _checkDone())) {
            _counter = 0
          } else {
            if (--_counter === 0) _done()
          }
        }
      } else {
        if (_counter > 0) {
          if (
            2 < _results.length &&
            (_result2 !== undefined && (_results.length = 3), (_results[2] = { result: _result2 }), _checkDone())
          ) {
            _counter = 0
          } else {
            if (--_counter === 0) _done()
          }
        }
      }
    })
  }
  if (_counter <= 0) break
  if (3 >= _results.length) {
    if (--_counter === 0) _done()
  } else {
    var _tap3 = _taps[3]
    _interceptors[0].tap(_tap3)
    var _fn3 = _x[3]
    _fn3(xxx, arg2, (_err3, _result3) => {
      if (_err3) {
        if (_counter > 0) {
          if (3 < _results.length && ((_results.length = 4), (_results[3] = { error: _err3 }), _checkDone())) {
            _counter = 0
          } else {
            if (--_counter === 0) _done()
          }
        }
      } else {
        if (_counter > 0) {
          if (
            3 < _results.length &&
            (_result3 !== undefined && (_results.length = 4), (_results[3] = { result: _result3 }), _checkDone())
          ) {
            _counter = 0
          } else {
            if (--_counter === 0) _done()
          }
        }
      }
    })
  }
  if (_counter <= 0) break
  if (4 >= _results.length) {
    if (--_counter === 0) _done()
  } else {
    var _tap4 = _taps[4]
    _interceptors[0].tap(_tap4)
    var _fn4 = _x[4]
    _fn4(xxx, arg2, (_err4, _result4) => {
      if (_err4) {
        if (_counter > 0) {
          if (4 < _results.length && ((_results.length = 5), (_results[4] = { error: _err4 }), _checkDone())) {
            _counter = 0
          } else {
            if (--_counter === 0) _done()
          }
        }
      } else {
        if (_counter > 0) {
          if (
            4 < _results.length &&
            (_result4 !== undefined && (_results.length = 5), (_results[4] = { result: _result4 }), _checkDone())
          ) {
            _counter = 0
          } else {
            if (--_counter === 0) _done()
          }
        }
      }
    })
  }
  if (_counter <= 0) break
  if (5 >= _results.length) {
    if (--_counter === 0) _done()
  } else {
    var _tap5 = _taps[5]
    _interceptors[0].tap(_tap5)
    var _fn5 = _x[5]
    _fn5(xxx, arg2, (_err5, _result5) => {
      if (_err5) {
        if (_counter > 0) {
          if (5 < _results.length && ((_results.length = 6), (_results[5] = { error: _err5 }), _checkDone())) {
            _counter = 0
          } else {
            if (--_counter === 0) _done()
          }
        }
      } else {
        if (_counter > 0) {
          if (
            5 < _results.length &&
            (_result5 !== undefined && (_results.length = 6), (_results[5] = { result: _result5 }), _checkDone())
          ) {
            _counter = 0
          } else {
            if (--_counter === 0) _done()
          }
        }
      }
    })
  }
} while (false)

从上面可以看出其跟 asyncParallelHook 一样通过 _counter 的方式去处理并行异步回调完成的问题,但是跟 asyncParallelHook 不一样的地方在于其执行钩子的回调不是在所有的异步回调完成(_counter === 0),而是

  1. 在某一个异步中 done(undefined,true)传入了第二个不为 undefined 的参数。

  2. 某一个异步中有错误产生 done(error)

这两种情况都会触发钩子的回调

那么其是如何判断是哪一个事件流触发的钩子回调且保留其 err 信息

从上面伪代码上看其核心就是

js
// 初始化一个当前事件流个数空间大小的数组
var _results = new Array(6)

var _checkDone = () => {
  for (var i = 0; i < _results.length; i++) {
    var item = _results[i]
    if (item === undefined) return false
    // 如果是`done(undefined,true)` 第二个参数不是undefined  触发钩子回调
    if (item.result !== undefined) {
      _callback(null, item.result)
      return true
    }
    // 如果是`done(error)` 第二个参数不是undefined  触发钩子回调
    if (item.error) {
      _callback(item.error)
      return true
    }
  }
  return false
}

可见其初始化一个当前事件流个数空间大小的数组,然后在每一个事件流回调中如果触发了 error 或者 第二个参数不是 undefined 都会去 _checkDone(),然后遍历找到对应的对象 { error: xxx , result : 第二个参数的值 }

上次更新: