diff --git a/lib/runtime/AsyncModuleRuntimeModule.js b/lib/runtime/AsyncModuleRuntimeModule.js index 137509a8a87..5da0ee4e9bd 100644 --- a/lib/runtime/AsyncModuleRuntimeModule.js +++ b/lib/runtime/AsyncModuleRuntimeModule.js @@ -20,12 +20,13 @@ class AsyncModuleRuntimeModule extends HelperRuntimeModule { const { runtimeTemplate } = this.compilation; const fn = RuntimeGlobals.asyncModule; return Template.asString([ - 'var webpackThen = typeof Symbol === "function" ? Symbol("webpack then") : "__webpack_then__";', + 'var webpackQueues = typeof Symbol === "function" ? Symbol("webpack queues") : "__webpack_queues__";', 'var webpackExports = typeof Symbol === "function" ? Symbol("webpack exports") : "__webpack_exports__";', 'var webpackError = typeof Symbol === "function" ? Symbol("webpack error") : "__webpack_error__";', - `var completeQueue = ${runtimeTemplate.basicFunction("queue", [ - "if(queue) {", + `var resolveQueue = ${runtimeTemplate.basicFunction("queue", [ + "if(queue && !queue.d) {", Template.indent([ + "queue.d = 1;", `queue.forEach(${runtimeTemplate.expressionFunction( "fn.r--", "fn" @@ -37,35 +38,26 @@ class AsyncModuleRuntimeModule extends HelperRuntimeModule { ]), "}" ])}`, - `var completeFunction = ${runtimeTemplate.expressionFunction( - "!--fn.r && fn()", - "fn" - )};`, - `var queueFunction = ${runtimeTemplate.expressionFunction( - "queue ? queue.push(fn) : completeFunction(fn)", - "queue, fn" - )};`, `var wrapDeps = ${runtimeTemplate.returningFunction( `deps.map(${runtimeTemplate.basicFunction("dep", [ 'if(dep !== null && typeof dep === "object") {', Template.indent([ - "if(dep[webpackThen]) return dep;", + "if(dep[webpackQueues]) return dep;", "if(dep.then) {", Template.indent([ "var queue = [];", + "queue.d = 0;", `dep.then(${runtimeTemplate.basicFunction("r", [ "obj[webpackExports] = r;", - "completeQueue(queue);", - "queue = 0;" + "resolveQueue(queue);" ])}, ${runtimeTemplate.basicFunction("e", [ "obj[webpackError] = e;", - "completeQueue(queue);", - "queue = 0;" + "resolveQueue(queue);" ])});`, "var obj = {};", - `obj[webpackThen] = ${runtimeTemplate.expressionFunction( - "queueFunction(queue, fn), dep['catch'](reject)", - "fn, reject" + `obj[webpackQueues] = ${runtimeTemplate.expressionFunction( + `fn(queue)`, + "fn" )};`, "return obj;" ]), @@ -73,55 +65,31 @@ class AsyncModuleRuntimeModule extends HelperRuntimeModule { ]), "}", "var ret = {};", - `ret[webpackThen] = ${runtimeTemplate.expressionFunction( - "completeFunction(fn)", - "fn" - )};`, + `ret[webpackQueues] = ${runtimeTemplate.emptyFunction()};`, "ret[webpackExports] = dep;", "return ret;" ])})`, "deps" )};`, `${fn} = ${runtimeTemplate.basicFunction("module, body, hasAwait", [ - "var queue = hasAwait && [];", + "var queue;", + "hasAwait && ((queue = []).d = 1);", + "if(queue) queue.moduleId = module.id;", + "var depQueues = new Set();", "var exports = module.exports;", "var currentDeps;", "var outerResolve;", "var reject;", - "var isEvaluating = true;", - "var nested = false;", - `var whenAll = ${runtimeTemplate.basicFunction( - "deps, onResolve, onReject", - [ - "if (nested) return;", - "nested = true;", - "onResolve.r += deps.length;", - `deps.map(${runtimeTemplate.expressionFunction( - "dep[webpackThen](onResolve, onReject)", - "dep, i" - )});`, - "nested = false;" - ] - )};`, `var promise = new Promise(${runtimeTemplate.basicFunction( "resolve, rej", - [ - "reject = rej;", - `outerResolve = ${runtimeTemplate.expressionFunction( - "resolve(exports), completeQueue(queue), queue = 0" - )};` - ] + ["reject = rej;", "outerResolve = resolve;"] )});`, "promise[webpackExports] = exports;", - `promise[webpackThen] = ${runtimeTemplate.basicFunction( - "fn, rejectFn", - [ - "if (isEvaluating) { return completeFunction(fn); }", - "if (currentDeps) whenAll(currentDeps, fn, rejectFn);", - "queueFunction(queue, fn);", - "promise['catch'](rejectFn);" - ] + `promise[webpackQueues] = ${runtimeTemplate.expressionFunction( + `queue && fn(queue), depQueues.forEach(fn), promise["catch"](${runtimeTemplate.emptyFunction()})`, + "fn" )};`, + "promise.moduleId = module.id;", "module.exports = promise;", `body(${runtimeTemplate.basicFunction("deps", [ "currentDeps = wrapDeps(deps);", @@ -133,21 +101,29 @@ class AsyncModuleRuntimeModule extends HelperRuntimeModule { ])})` )}`, `var promise = new Promise(${runtimeTemplate.basicFunction( - "resolve, reject", + "resolve", [ `fn = ${runtimeTemplate.expressionFunction( - "resolve(getResult)" + "resolve(getResult)", + "" )};`, "fn.r = 0;", - "whenAll(currentDeps, fn, reject);" + `var fnQueue = ${runtimeTemplate.expressionFunction( + "q !== queue && !depQueues.has(q) && (depQueues.add(q), q && !q.d && (fn.r++, q.push(fn)))", + "q" + )};`, + `currentDeps.map(${runtimeTemplate.expressionFunction( + "dep[webpackQueues](fnQueue)", + "dep" + )});` ] )});`, "return fn.r ? promise : getResult();" ])}, ${runtimeTemplate.expressionFunction( - "err && reject(promise[webpackError] = err), outerResolve()", + "(err ? reject(promise[webpackError] = err) : outerResolve(exports)), resolveQueue(queue)", "err" )});`, - "isEvaluating = false;" + "queue && (queue.d = 0);" ])};` ]); } diff --git a/test/__snapshots__/StatsTestCases.basictest.js.snap b/test/__snapshots__/StatsTestCases.basictest.js.snap index 1c50e8a658d..2cf23d3ed27 100644 --- a/test/__snapshots__/StatsTestCases.basictest.js.snap +++ b/test/__snapshots__/StatsTestCases.basictest.js.snap @@ -4611,8 +4611,8 @@ webpack x.x.x compiled with 1 warning in X ms" `; exports[`StatsTestCases should print correct stats for wasm-explorer-examples-sync 1`] = ` -"assets by path *.js 22.2 KiB - asset bundle.js 16.7 KiB [emitted] (name: main) +"assets by path *.js 21.8 KiB + asset bundle.js 16.3 KiB [emitted] (name: main) asset 325.bundle.js 3.9 KiB [emitted] asset 795.bundle.js 557 bytes [emitted] asset 526.bundle.js 366 bytes [emitted] (id hint: vendors) @@ -4628,8 +4628,8 @@ assets by path *.wasm 1.37 KiB asset 0301cb3f9f4151b567f5.module.wasm 120 bytes [emitted] [immutable] chunk (runtime: main) 20.bundle.js 50 bytes (javascript) 531 bytes (webassembly) [rendered] ./duff.wasm 50 bytes (javascript) 531 bytes (webassembly) [built] [code generated] -chunk (runtime: main) bundle.js (main) 586 bytes (javascript) 9.49 KiB (runtime) [entry] [rendered] - runtime modules 9.49 KiB 11 modules +chunk (runtime: main) bundle.js (main) 586 bytes (javascript) 9.19 KiB (runtime) [entry] [rendered] + runtime modules 9.19 KiB 11 modules ./index.js 586 bytes [built] [code generated] chunk (runtime: main) 189.bundle.js 50 bytes (javascript) 156 bytes (webassembly) [rendered] ./Q_rsqrt.wasm 50 bytes (javascript) 156 bytes (webassembly) [built] [code generated] @@ -4643,7 +4643,7 @@ chunk (runtime: main) 526.bundle.js (id hint: vendors) 34 bytes [rendered] split chunk (runtime: main) 795.bundle.js 110 bytes (javascript) 444 bytes (webassembly) [rendered] ./fact.wasm 50 bytes (javascript) 154 bytes (webassembly) [built] [code generated] ./fast-math.wasm 60 bytes (javascript) 290 bytes (webassembly) [built] [code generated] -runtime modules 9.49 KiB 11 modules +runtime modules 9.19 KiB 11 modules cacheable modules 2.31 KiB (javascript) 1.37 KiB (webassembly) webassembly modules 310 bytes (javascript) 1.37 KiB (webassembly) ./Q_rsqrt.wasm 50 bytes (javascript) 156 bytes (webassembly) [built] [code generated] diff --git a/test/cases/async-modules/runtime-performance/async.js b/test/cases/async-modules/runtime-performance/async.js new file mode 100644 index 00000000000..03ed4ae4663 --- /dev/null +++ b/test/cases/async-modules/runtime-performance/async.js @@ -0,0 +1,2 @@ +await 1; +export default 1; diff --git a/test/cases/async-modules/runtime-performance/index.js b/test/cases/async-modules/runtime-performance/index.js new file mode 100644 index 00000000000..1aca8000cd4 --- /dev/null +++ b/test/cases/async-modules/runtime-performance/index.js @@ -0,0 +1,5 @@ +it("should not take too long to evaluate nested async modules", async () => { + const start = Date.now(); + await import(/* webpackMode: "eager" */ "./loader.js?i=40!./loader.js"); + expect(Date.now() - start).toBeLessThan(100); +}); diff --git a/test/cases/async-modules/runtime-performance/loader.js b/test/cases/async-modules/runtime-performance/loader.js new file mode 100644 index 00000000000..ea46f2bea9b --- /dev/null +++ b/test/cases/async-modules/runtime-performance/loader.js @@ -0,0 +1,14 @@ +/** @type {import("../../../../").LoaderDefinition<{ i: string }>} */ +module.exports = function () { + const options = this.getOptions(); + const i = +options.i; + let src = `import n from "./async.js";\n`; + if (i > 0) { + src += `import a from "./loader.js?i=${i - 1}&a!./loader.js";\n`; + src += `import b from "./loader.js?i=${i - 1}&b!./loader.js";\n`; + src += `export default n + a + b;\n`; + } else { + src += `export default n;\n`; + } + return src; +};