webrpack之 tapable

webpack之tapable

近期研究webpack loader和plugin编写,发现涉及到tapable,闲来无事翻翻源码结合使用方法记录一下webpack

原文 github.com/webpack/tap…git

1、钩子类型

tapable提供了不少钩子(Hook classes)github

const {
	SyncHook,                    // 同步钩子 从上到下顺序执行
	SyncBailHook,                // 同步早退钩子 从上到下顺序执行,遇到返回值不是undefined的注册函数时中止执行
	SyncWaterfallHook,           // 同步瀑布钩子 从上到下执行,依次将返回值传递给下一个函数
	SyncLoopHook,                // 同步循环钩子 从上到下执行,某个函数可能会执行好几遍,当返回值是undefined会继续执行下个函数
	AsyncParallelHook,           // 异步并发钩子 异步并行
	AsyncParallelBailHook,       // 异步并发可早退钩子 异步并行熔断
	AsyncSeriesHook,             // 异步顺序钩子   异步串行
	AsyncSeriesBailHook,         // 异步顺序可早退钩子 异步串行熔断
	AsyncSeriesWaterfallHook     // 异步顺序瀑布钩子   异步串行值传递【瀑布】
 } = require("tapable");
复制代码

2、搭建项目

github.com/17139313271…web

3、钩子使用方法

3.1 同步钩子-SyncHook

import { SyncHook } from '../table/lib';

const hook = new SyncHook(); // 建立钩子对象
hook.tap('logPlugin', () => console.log('注册了')); // tap方法注册钩子回调
hook.call(); // call方法调用钩子,打印出‘被勾了’三个字
复制代码

3.2 同步早退钩子-SyncBailHook

SyncBailHook就是根据每一步返回的值来决定要不要继续往下走,若是return了一个非undefined的值 那就不会往下走,注意 若是什么都不return 也至关于return了一个undefined。数组

import { SyncBailHook } from '../table/lib';

const hook = new SyncBailHook();
hook.tap('SyncBailHook1', () => console.log(`钩子1`));
hook.tap('SyncBailHook2', () => {console.log(`钩子2`) ; return 1});
hook.tap('SyncBailHook3', () => console.log(`钩子3`));

hook.call(); // 会打印‘钩子1’‘钩子2’‘钩子3’
复制代码

3.3 同步瀑布钩子-SyncWaterfallHook

它的每一步都依赖上一步的执行结果,也就是上一步return的值就是下一步的参数。promise

import { SyncWaterfallHook  } from '../table/lib';

const hook = new SyncWaterfallHook(["newSpeed"]);
hook.tap('SyncWaterfallHook1', (speed) => { console.log(`增长到${speed}`); return speed + 100; });
hook.tap('SyncWaterfallHook2', (speed) => { console.log(`增长到${speed}`); return speed + 50; });
hook.tap('SyncWaterfallHook3', (speed) => { console.log(`增长到${speed}`); });

hook.call(50); // 打印‘增长到50’‘增长到150’‘增长到200’
复制代码

3.4 同步循环钩子 -SyncLoopHook

SyncLoopHook是同步的循环钩子,它的插件若是返回一个非undefined。就会一直执行这个插件的回调函数,直到它返回undefined。markdown

import { SyncLoopHook } from '../table/lib';

let index = 0;
const hook = new SyncLoopHook();
hook.tap('startPlugin1', () => {
    console.log(`执行`);
    if (index < 5) {
        index++;
        return 1;
    }
}); 

hook.tap('startPlugin2', () => {
    console.log(`执行2`);
});

hook.call(); // 打印‘执行’6次,打印‘执行2’一次。
复制代码

3.5异步并发钩子-AsyncParallelHook

当全部的异步任务执行结束后,再最终的回调中执行接下来的代码并发

import { AsyncParallelHook } from '../table/lib';

const hook = new AsyncParallelHook();
hook.tapAsync('calculateRoutesPlugin1', (callback) => {
    setTimeout(() => {
        console.log('异步事件1');
        callback();
    }, 1000);
});

hook.tapAsync('calculateRoutesPlugin2', (callback) => {
    setTimeout(() => {
        console.log('异步事件2');
        callback();
    }, 2000);
});

hook.callAsync(() => { console.log('最终的回调'); }); // 会在1s的时候打印‘异步事件1’。2s的时候打印‘异步事件2’。紧接着打印‘最终的回调’
复制代码

3.6异步并发可早退钩子-AsyncParallelBailHook

import { AsyncParallelBailHook } from '../table/lib';

const hook = new AsyncParallelBailHook();
hook.tapAsync('calculateRoutesPlugin1', (callback) => {
    setTimeout(() => {
        console.log('异步事件1');
        callback(1);
    }, 1000);
});

hook.tapAsync('calculateRoutesPlugin2', (callback) => {
    setTimeout(() => {
        console.log('异步事件2');
        callback();
    }, 2000);
});

hook.callAsync((result) => { console.log('最终的回调',result); }); // 会在1s的时候打印‘异步事件1’,紧接着打印‘最终的回调’,2s的时候打印‘异步事件2’。
复制代码

3.7 异步顺序钩子- AsyncSeriesHook

import { AsyncSeriesHook } from '../table/lib';

const hook = new AsyncSeriesHook();
hook.tapPromise('calculateRoutesPlugin1', () => {
    return new Promise((resolve, reject) => {
        setTimeout(() => {
            console.log('异步事件1');
            resolve();
        }, 1000);
    });
});

hook.tapPromise('calculateRoutesPlugin2', () => {
    return new Promise((resolve, reject) => {
        setTimeout(() => {
            console.log('异步事件2');
            resolve();
        }, 2000);
    });
});

hook.then(() => { console.log('最终的回调'); });
// 1s事后,打印异步事件1,再过2s(而不是到了第2s,而是到了第3s),打印异步事件2,再立马打印最终的回调。
复制代码

3.8 异步顺序可早退钩子-AsyncSeriesBailHook

import { AsyncSeriesBailHook } from '../table/lib';

const hook = new AsyncSeriesBailHook();
hook.tapPromise('calculateRoutesPlugin1', () => {
    return new Promise((resolve, reject) => {
        setTimeout(() => {
            console.log('异步事件1');
            resolve(1);
        }, 1000);
    });
});

hook.tapPromise('calculateRoutesPlugin2', () => {
    return new Promise((resolve, reject) => {
        setTimeout(() => {
            console.log('异步事件2');
            resolve();
        }, 2000);
    });
});

hook.then(() => { console.log('最终的回调'); });
// 1s事后,打印异步事件1,立马打印最终的回调,不会再执行异步事件2了。
复制代码

3.9 异步顺序瀑布钩子-AsyncSeriesWaterfallHook

import { AsyncSeriesWaterfallHook } from '../table/lib';

const hook = new AsyncSeriesWaterfallHook(['args']);
hook.tapPromise('calculateRoutesPlugin1', (result) => {
    return new Promise((resolve, reject) => {
        setTimeout(() => {
            console.log('异步事件1', result);

            resolve(result+1);
        }, 1000);
    });
});

hook.tapPromise('calculateRoutesPlugin2', (result) => {
    return new Promise((resolve, reject) => {
        setTimeout(() => {
            console.log('异步事件2', result);
            resolve(result+2);
        }, 2000);
    });
});

hook.promise(12).then((result) => { console.log('最终的回调' + result); });
// // 1s事后,打印异步事件1 12,再过2s打印异步事件2 13,而后立马打印最终的回调 15。
复制代码

4、从源码分析钩子实现原理

4.1 流程分析

  1. tapable 源码使用工厂模式和模板模式,Hook.js 抽离了功能函数,便于其余钩子函数继承,HookCodeFactory.js则使用模板模式,根据钩子函数的不一样生成不一样的执行函数返回调用的_call()异步

  2. 注册过程async

    tapable注册方式有三种: tap、tapAsync和tapPromise,但过程基本都同样,都会调用 _insert()

    HOOK.js
    
    
    _insert(item) {
    	this._resetCompilation();
    	let before;
    	if (typeof item.before === "string") before = new Set([item.before]);
    	else if (Array.isArray(item.before)) {
    		before = new Set(item.before);
    	}
    	let stage = 0;
    	if (typeof item.stage === "number") stage = item.stage;
    	let i = this.taps.length;
    	while (i > 0) {
    		i--;
    		const x = this.taps[i];
    		this.taps[i + 1] = x;
    		const xStage = x.stage || 0;
    		if (before) {
    			if (before.has(x.name)) {
    				before.delete(x.name);
    				continue;
    			}
    			if (before.size > 0) {
    				continue;
    			}
    		}
    			if (xStage > stage) {
    				continue;
    			}
    			i++;
    			break;
    	}
    	this.taps[i] = item;
    	console.log(this.taps)
    }
     1. item 对象是根据注册方式的不一样生成不一样的对象:
         tap:              { type: "sync", fn: fn ,name: name}
         tapAsync:         { type: "async", fn: fn ,name: name}
         tapPromise:       { type: "promise", fn: fn ,name: name}
         后面的函数执行都会按照注册的type类型来执行
     2. _insert() 做用: 根据注册类型的不一样生成不一样的对象并存储在 taps[] 中    
    复制代码
  3. 调用过程

    3.1tapable调用方式有三种: call、promise和callAsync,调用createCompileDelegate(), 传入相应的类型

    Hook.js
    
    function createCompileDelegate(name, type) {
    	return function lazyCompileHook(...args) {
    		this[name] = this._createCall(type);
    		console.log(this[name])
    		return this[name](...args);
    	};
    }
    
    .....
    	_createCall(type) {
    		return this.compile({
    			taps: this.taps,
    			interceptors: this.interceptors,
    			args: this._args,
    			type: type
    		});
    	}
    ....
    
    _createCall() 函数将参数传给各个钩子的compile()函数,用来生成回调函数  
    复制代码

    3.2各个钩子函数都会继承两个函数:

​ a. conent() 根据钩子函数的不一样生成不一样的执行函数即 _call()

​ b. compile(), 此函数会调用 HookCodeFactory.js 的两个函数 setup() 和 create()

setup(instance, options) {
	instance._x = options.taps.map(t => t.fn);
}
// 	将 optiuon数组保存的注册的函数过滤出来赋值给当前钩子函数的 ._X
create(options) {
  this.init(options);
  switch (this.options.type) {
	 case "sync":
		fn = new Function(
			this.args(),
			'"use strict";\n' +
			 this.header() +
			 this.content({
			   onError: err => `throw ${err};\n`,
			   onResult: result => `return ${result};\n`,
			   resultReturns: true,
			   onDone: () => "",
				 rethrowIfPossible: true
			})
		);
		break;
        .....
	}
	this.deinit();
	return fn;
}

init(options) {
	this.options = options;
	this._args = options.args.slice();	
}

1. init()函数 
  这个函数很简单,但注意 _args 这个数组的由来, 他是lazyCompileHook()传入的参数,即 call、promise和callAsync 这三个回调函数传入的参数
2.create 根据注册钩子的类型调用各个钩子函数的 content()生成执行函数 fn
也就是说咱们调用(call())的时候实际上是执行下面这样一个函数,
anonymous() {
 "use strict";
  var _context;
  var _x = this._x;
  var _fn0 = _x[0];
  _fn0();
  var _fn1 = _x[1];  // 多个注册函数会生成多个
  _fn1();
}
复制代码

4.2 SyncHook的实现

//content函数
content({ onError, onDone, rethrowIfPossible }) {
		return this.callTapsSeries({
			onError: (i, err) => onError(err),
			onDone,
			rethrowIfPossible
		});
	}

// 生成的执行函数
anonymous() {
 "use strict";
  var _context;
  var _x = this._x;
  var _fn0 = _x[0];
   _fn0();
   var _fn1 = _x[1];
   _fn1();
}
复制代码

SyncHook 钩子生成多个 fn()函数,call()调用的时候挨个执行,比较简单

4.3 SyncBailHook

前面介绍使用方法时说过此钩子根据返回值判断是否继续执行,它的内部构造与 SyncHook稍有不一样:

// content 函数
content({ onError, onResult, resultReturns, onDone, rethrowIfPossible }) {
		return this.callTapsSeries({
			onError: (i, err) => onError(err),
			onResult: (i, result, next) =>
				`if(${result} !== undefined) {\n${onResult(
					result
				)};\n} else {\n${next()}}\n`,
			resultReturns,
			onDone,
			rethrowIfPossible
		});
}

//生成的执行函数
(function anonymous() {
  "use strict";
   var _context;
   var _x = this._x;
   var _fn0 = _x[0];
   var _result0 = _fn0();
   if(_result0 !== undefined) {   // 递归生成判断条件
     return _result0;
   } else {
    var _fn1 = _x[1];
    var _result1 = _fn1();
    if(_result1 !== undefined) {
       return _result1;
    } else {
      var _fn2 = _x[2];
      var _result2 = _fn2();
      if(_result2 !== undefined) {
        return _result2;
      } else {}
    }
  }
})
复制代码

SyncBailHook 的 content函数 的 onResult 多一个判断条件,判断上一个钩子函数的执行结果来决定是否继续执行

4.4 SyncWaterfallHook

//content函数
content({ onError, onResult, resultReturns, rethrowIfPossible }) {
		return this.callTapsSeries({
			onError: (i, err) => onError(err),
			onResult: (i, result, next) => {
				let code = "";
				code += `if(${result} !== undefined) {\n`;
				code += `${this._args[0]} = ${result};\n`;  //与SyncBailHook 相比多加赋值的操做
				code += `}\n`;
				code += next(); 
				return code;
			},
			onDone: () => onResult(this._args[0]),
			doneReturns: resultReturns,
			rethrowIfPossible
		});
}
//生成的函数
(function anonymous(newSpeed) {
  "use strict";
  var _context;
  var _x = this._x;
  var _fn0 = _x[0];
  var _result0 = _fn0(newSpeed);
  if(_result0 !== undefined) {
    newSpeed = _result0;
   }
   var _fn1 = _x[1];
   var _result1 = _fn1(newSpeed);
  if(_result1 !== undefined) {
    newSpeed = _result1;
   }
   var _fn2 = _x[2];
  var _result2 = _fn2(newSpeed);
  if(_result2 !== undefined) {
    newSpeed = _result2;
  }
  return newSpeed;
})
复制代码

SyncWaterfallHook的content 的onResult 与SyncBailHook相比多赋值的操做,判断是否有返回值,若是有返回值就将返回值传给下一个执行函数

4.5 SyncLoopHook

// content
content({ onError, onDone, rethrowIfPossible }) {
		return this.callTapsLooping({
			onError: (i, err) => onError(err),
			onDone,
			rethrowIfPossible
		});
}
//生成的函数
(function anonymous() {
  "use strict";
   var _context;
   var _x = this._x;
   var _loop;
  do {
    _loop = false;
   var _fn0 = _x[0];
   var _result0 = _fn0();
   if(_result0 !== undefined) {     //根据返回值判断是否继续执行
     _loop = true;
   } else {
     var _fn1 = _x[1];
     var _result1 = _fn1();
     if(_result1 !== undefined) {
        _loop = true;
      } else {
       if(!_loop) {}
      }
   }
} while(_loop);
})


callTapsLooping({ onError, onDone, rethrowIfPossible }) {
 if (this.options.taps.length === 0) return onDone();
	const syncOnly = this.options.taps.every(t => t.type === "sync");
	let code = "";
	if (!syncOnly) {
		code += "var _looper = () => {\n";
		code += "var _loopAsync = false;\n";
	}
	   code += "var _loop;\n";
	   code += "do {\n";
	   code += "_loop = false;\n";
	   for (let i = 0; i < this.options.interceptors.length; i++) {
		 const interceptor = this.options.interceptors[i];
		 if (interceptor.loop) {
			code += `${this.getInterceptor(i)}.loop(${this.args({
				before: interceptor.context ? "_context" : undefined
			})});\n`;
		  }
	   }
	  code += this.callTapsSeries({
		onError,
	  onResult: (i, result, next, doneBreak) => {
		 let code = "";
		 code += `if(${result} !== undefined) {\n`;
		 code += "_loop = true;\n";
		 if (!syncOnly) code += "if(_loopAsync) _looper();\n";
		 code += doneBreak(true);
		 code += `} else {\n`;
		 code += next();
		 code += `}\n`;
		 return code;
	    },
		....
}
复制代码

注意 SyncLoopHook 的content 函数与上面三个不一样,SyncLoopHook 的content 调用的是 callTapsLooping()函数,而其余的同步钩子调用的是 callTapsSeries(),

4.6AsyncParallelHook

// content函数
content({ onError, onDone }) {
		return this.callTapsParallel({
			onError: (i, err, done, doneBreak) => onError(err) + doneBreak(true),
			onDone
		});
}
//生成的函数
(function anonymous(_callback) {
    "use strict";
    var _context;
    var _x = this._x;
    do {
        var _counter = 2;
        var _done = () => {
            _callback();
        };
        if(_counter <= 0) break;
        var _fn0 = _x[0];
        _fn0(_err0 => {
            if(_err0) {                  //若是有参数传入
                if(_counter > 0) {
                    _callback(_err0);
                    _counter = 0;
                }
            } else {
                if(--_counter === 0) _done();
            }
        });
        if(_counter <= 0) break;
        var _fn1 = _x[1];
        _fn1(_err1 => {
            if(_err1) {
                if(_counter > 0) {
                    _callback(_err1);
                    _counter = 0;
                }
            } else {
                if(--_counter === 0) _done();
            }
        });
    } while(false);
})
复制代码

根据AsyncParallelHook 钩子生成的函数咱们能够看出他是判断err 有值时会走onError,执行 callback(),若是没有任何参数则当 _counter减为0即最后一个注册函数时执行callback()

4.7AsyncParallelBailHook

  • // content
    content({ onError, onResult, onDone }) {
    		let code = "";
    		code += `var _results = new Array(${this.options.taps.length});\n`;
    		code += "var _checkDone = () => {\n";
    		code += "for(var i = 0; i < _results.length; i++) {\n";
    		code += "var item = _results[i];\n";
    		code += "if(item === undefined) return false;\n";
    		code += "if(item.result !== undefined) {\n";
    		code += onResult("item.result");
    		code += "return true;\n";
    		code += "}\n";
    		code += "if(item.error) {\n";
    		code += onError("item.error");
    		code += "return true;\n";
    		code += "}\n";
    		code += "}\n";
    		code += "return false;\n";
    		code += "}\n";
    		code += this.callTapsParallel({
    			onError: (i, err, done, doneBreak) => {
    				let code = "";
    				code += `if(${i} < _results.length && ((_results.length = ${i +
    					1}), (_results[${i}] = { error: ${err} }), _checkDone())) {\n`;
    				code += doneBreak(true);
    				code += "} else {\n";
    				code += done();
    				code += "}\n";
    				return code;
    			},
    			onResult: (i, result, done, doneBreak) => {
    				let code = "";
    				code += `if(${i} < _results.length && (${result} !== undefined && (_results.length = ${i +
    					1}), (_results[${i}] = { result: ${result} }), _checkDone())) {\n`;
    				code += doneBreak(true);
    				code += "} else {\n";
    				code += done();
    				code += "}\n";
    				return code;
    			},
    			onTap: (i, run, done, doneBreak) => {
    				let code = "";
    				if (i > 0) {
    					code += `if(${i} >= _results.length) {\n`;
    					code += done();
    					code += "} else {\n";
    				}
    				code += run();
    				if (i > 0) code += "}\n";
    				return code;
    			},
    			onDone
    		});
    		return code;
    }  
    
    //生成的函数
    (function anonymous(_callback
    ) {
        "use strict";
        var _context;
        var _x = this._x;
        var _results = new Array(2);
        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 = 2;
            var _done = () => {
                _callback();
            };
            if(_counter <= 0) break;
            var _fn0 = _x[0];
            _fn0((_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 _fn1 = _x[1];
                _fn1((_err1, _result1) => {
                    console.log('_err',_err1)
                    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();
                            }
                        }
                    }
                });
            }
        } while(false);
    
    })
    复制代码

网上有文章说这个钩子在第一个注册的插件执行完毕熔断,但好像不是这样,这个函数有个意思的地方,当err有参数传入时走的是 onError,没有参数走的是onResult

callTap(tapIndex, { onError, onResult, onDone, rethrowIfPossible }) {
	....
	switch (tap.type) {
			case "sync":
			
			case "async":
				let cbCode = "";
				if (onResult) cbCode += `(_err${tapIndex}, _result${tapIndex}) => {\n`;
				else cbCode += `_err${tapIndex} => {\n`;
				cbCode += `console.log('_err',_err${tapIndex})\n`;
				cbCode += `if(_err${tapIndex}) {\n`;
				cbCode += onError(`_err${tapIndex}`);
				cbCode += "} else {\n";
				if (onResult) {
					cbCode += onResult(`_result${tapIndex}`);
				}
				if (onDone) {
					cbCode += onDone();
				}
				cbCode += "}\n";
				cbCode += "}";
				code += `_fn${tapIndex}(${this.args({
					before: tap.context ? "_context" : undefined,
					after: cbCode
				})});\n`;
				break;
			case "promise":
	       .....
		}
		return code;
}

这里判断err即有参数传入时执行的是onError,因此注册以后传入参数时其实执行的是_checkDone()循环判断_results的error有值时执行callback()
  if(_counter > 0) {
     if(0 < _results.length && ((_results.length = 1), (_results[0] = { error: _err0 }), _checkDone())) {
      _counter = 0;
     } else {
       if(--_counter === 0) _done();
      }
  }
复制代码

4.8AsyncSeriesHook

//content 
content({ onError, onDone }) {
		return this.callTapsSeries({
			onError: (i, err, next, doneBreak) => onError(err) + doneBreak(true),
			onDone
		});
}
//生成的函数
(function anonymous(
) {
    "use strict";
    return new Promise((_resolve, _reject) => {
        var _sync = true;
        function _error(_err) {
            if(_sync)
                _resolve(Promise.resolve().then(() => { throw _err; }));
            else
                _reject(_err);
        };
        var _context;
        var _x = this._x;
        function _next0() {
            var _fn1 = _x[1];
            var _hasResult1 = false;
            var _promise1 = _fn1();
            if (!_promise1 || !_promise1.then)
                throw new Error('Tap function (tapPromise) did not return promise (returned ' + _promise1 + ')');
            _promise1.then(_result1 => {
                _hasResult1 = true;
                _resolve();
            }, _err1 => {
                if(_hasResult1) throw _err1;
                _error(_err1);
            });
        }
        var _fn0 = _x[0];
        var _hasResult0 = false;
        var _promise0 = _fn0();
        if (!_promise0 || !_promise0.then)
            throw new Error('Tap function (tapPromise) did not return promise (returned ' + _promise0 + ')');
        _promise0.then(_result0 => {
            _hasResult0 = true;
            _next0();
        }, _err0 => {
            if(_hasResult0) throw _err0;
            _error(_err0);
        });
        _sync = false;
    });

})
复制代码

AsyncSeriesHook 调用的是callTapsSeries 生成的执行函数,原理比较简单,就是当一个promise执行完以后再去执行另外一个

4.9AsyncSeriesBailHook

// content
content({ onError, onResult, resultReturns, 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`,
			resultReturns,
			onDone
		});
}
//生成的函数
(function anonymous() {
    "use strict";
    return new Promise((_resolve, _reject) => {
        var _sync = true;
        function _error(_err) {
            if(_sync)
                _resolve(Promise.resolve().then(() => { throw _err; }));
            else
                _reject(_err);
        };
        var _context;
        var _x = this._x;
        function _next0() {
            var _fn1 = _x[1];
            var _hasResult1 = false;
            var _promise1 = _fn1();
            if (!_promise1 || !_promise1.then)
                throw new Error('Tap function (tapPromise) did not return promise (returned ' + _promise1 + ')');
            _promise1.then(_result1 => {
                _hasResult1 = true;
                if(_result1 !== undefined) {
                    _resolve(_result1);
                    ;
                } else {
                    _resolve();
                }
            }, _err1 => {
                if(_hasResult1) throw _err1;
                _error(_err1);
            });
        }
        var _fn0 = _x[0];
        var _hasResult0 = false;
        var _promise0 = _fn0();
        if (!_promise0 || !_promise0.then)
            throw new Error('Tap function (tapPromise) did not return promise (returned ' + _promise0 + ')');
        _promise0.then(_result0 => {
            _hasResult0 = true;
            if(_result0 !== undefined) {
                _resolve(_result0);
                ;
            } else {
                _next0();
            }
        }, _err0 => {
            if(_hasResult0) throw _err0;
            _error(_err0);
        });
        _sync = false;
    });

})

复制代码

这个钩子其实和SyncBailHook的实现是同样的,多一步判断返回值,若是有返回值就直接抛出了

4.10AsyncSeriesWaterfallHook

content({ onError, onResult, onDone }) {
		return this.callTapsSeries({
			onError: (i, err, next, doneBreak) => onError(err) + doneBreak(true),
			onResult: (i, result, next) => {
				let code = "";
				code += `if(${result} !== undefined) {\n`;
				code += `${this._args[0]} = ${result};\n`;
				code += `}\n`;
				code += next();
				return code;
			},
			onDone: () => onResult(this._args[0])
		});
}
//生成的函数
(function anonymous(home
) {
    "use strict";
    return new Promise((_resolve, _reject) => {
        var _sync = true;
        function _error(_err) {
            if(_sync)
                _resolve(Promise.resolve().then(() => { throw _err; }));
            else
                _reject(_err);
        };
        var _context;
        var _x = this._x;
        function _next0() {
            var _fn1 = _x[1];
            var _hasResult1 = false;
            var _promise1 = _fn1(home);
            if (!_promise1 || !_promise1.then)
                throw new Error('Tap function (tapPromise) did not return promise (returned ' + _promise1 + ')');
            _promise1.then(_result1 => {
                _hasResult1 = true;
                if(_result1 !== undefined) {
                    home = _result1;
                }
                _resolve(home);
            }, _err1 => {
                if(_hasResult1) throw _err1;
                _error(_err1);
            });
        }
        var _fn0 = _x[0];
        var _hasResult0 = false;
        var _promise0 = _fn0(home);
        if (!_promise0 || !_promise0.then)
            throw new Error('Tap function (tapPromise) did not return promise (returned ' + _promise0 + ')');
        _promise0.then(_result0 => {
            _hasResult0 = true;
            if(_result0 !== undefined) {
                home = _result0;
            }
            _next0();
        }, _err0 => {
            if(_hasResult0) throw _err0;
            _error(_err0);
        });
        _sync = false;
    });

})
复制代码

与SyncWaterfallHook 同样,多一步赋值过程