近期研究webpack loader和plugin編寫,發現涉及到tapable,閒來無事翻翻源碼結合使用方法記錄一下webpack
tapable提供了不少鉤子(Hook classes)github
const {
SyncHook, // 同步鉤子 從上到下順序執行
SyncBailHook, // 同步早退鉤子 從上到下順序執行,遇到返回值不是undefined的註冊函數時中止執行
SyncWaterfallHook, // 同步瀑布鉤子 從上到下執行,依次將返回值傳遞給下一個函數
SyncLoopHook, // 同步循環鉤子 從上到下執行,某個函數可能會執行好幾遍,當返回值是undefined會繼續執行下個函數
AsyncParallelHook, // 異步併發鉤子 異步並行
AsyncParallelBailHook, // 異步併發可早退鉤子 異步並行熔斷
AsyncSeriesHook, // 異步順序鉤子 異步串行
AsyncSeriesBailHook, // 異步順序可早退鉤子 異步串行熔斷
AsyncSeriesWaterfallHook // 異步順序瀑布鉤子 異步串行值傳遞【瀑布】
} = require("tapable");
複製代碼
import { SyncHook } from '../table/lib';
const hook = new SyncHook(); // 建立鉤子對象
hook.tap('logPlugin', () => console.log('註冊了')); // tap方法註冊鉤子回調
hook.call(); // call方法調用鉤子,打印出‘被勾了’三個字
複製代碼
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’
複製代碼
它的每一步都依賴上一步的執行結果,也就是上一步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’
複製代碼
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’一次。
複製代碼
當全部的異步任務執行結束後,再最終的回調中執行接下來的代碼併發
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’。緊接着打印‘最終的回調’
複製代碼
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’。
複製代碼
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,再立馬打印最終的回調。
複製代碼
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了。
複製代碼
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。
複製代碼
tapable 源碼使用工廠模式和模板模式,Hook.js 抽離了功能函數,便於其餘鉤子函數繼承,HookCodeFactory.js則使用模板模式,根據鉤子函數的不一樣生成不一樣的執行函數返回調用的_call()異步
註冊過程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.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();
}
複製代碼
//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()調用的時候挨個執行,比較簡單
前面介紹使用方法時說過此鉤子根據返回值判斷是否繼續執行,它的內部構造與 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 多一個判斷條件,判斷上一個鉤子函數的執行結果來決定是否繼續執行
//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相比多賦值的操做,判斷是否有返回值,若是有返回值就將返回值傳給下一個執行函數
// 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(),
// 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()
// 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();
}
}
複製代碼
//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執行完以後再去執行另外一個
// 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的實現是同樣的,多一步判斷返回值,若是有返回值就直接拋出了
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 同樣,多一步賦值過程