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 同樣,多一步賦值過程

相關文章
相關標籤/搜索