Description
- Version: v8.9.4
- Platform: Linux stola-ThinkPad 3.16.0-38-generic io.js on The Changelog! #52~14.04.1-Ubuntu SMP Fri May 8 09:43:57 UTC 2015 x86_64 x86_64 x86_64 GNU/Linux
- Subsystem: esm
The implementation of module loading depends on subtleties of the order of promise jobs (microtasks) in V8. In fact, I believe that some tests (like test-esm-namespace
) pass because of issue 6007 in V8 only. I tried to run these tests on our alternative version of Node.js that embeds a different JavaScript engine and some of es-module
tests failed with
Error: linking error, dependency promises must be resolved on instantiate
at checkComplete (internal/loader/ModuleJob.js:75:27)
at internal/loader/ModuleJob.js:58:11
The root of the problem seems to be in ModuleJob.js
. ModuleWrap::ResolveCallback
assumes/checks that promises that correspond to dependencies are resolved. Unfortunately, the callback (invoked through module.instantiate()
) is triggered when moduleJob.linked
promise is resolved. It is not triggered when all the promises created by ModuleWrap::Link
are resolved. See the relevant part of ModuleJob.js
:
const linked = async () => {
const dependencyJobs = [];
({ module: this.module,
reflect: this.reflect } = await this.modulePromise);
this.module.link(async (dependencySpecifier) => {
const dependencyJobPromise =
this.loader.getModuleJob(dependencySpecifier, url);
dependencyJobs.push(dependencyJobPromise);
const dependencyJob = await dependencyJobPromise;
return (await dependencyJob.modulePromise).module;
});
return SafePromise.all(dependencyJobs);
};
this.linked = linked();
moduleJob.linked
is resolved when all dependencyJobs
are resolved. Unfortunately, the promises created by ModuleWrap::Link
do not correspond to dependencyJobs
. They correspond to the body of the async function passed to module.link
. When the last dependencyJob
(aka dependencyJobPromise
) is resolved then SafePromise.all(dependencyJob)
(aka moduleJob.linked
) is resolved (and checkComplete
/module.instantiate
/ResolveCallback
is triggered). This can happen before the whole body of the async function is finished (and the promise checked by ModuleWrap::ResolveCallback
is resolved).
I think that the code should be rewritten such that there is a proper promise-based dependency (through Promise.prototype.then
) between module.install
and the promises used by ModuleWrap::ResolveCallback
.