From f9f73d2caf41af9f2606073321faf9d75e92d1ca Mon Sep 17 00:00:00 2001 From: Andrea Giammarchi Date: Mon, 23 Jan 2017 20:34:48 +0000 Subject: [PATCH 1/2] Asynchronous module.import(path):Promise --- XXX-module-import.md | 242 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 242 insertions(+) create mode 100644 XXX-module-import.md diff --git a/XXX-module-import.md b/XXX-module-import.md new file mode 100644 index 0000000..a9133b9 --- /dev/null +++ b/XXX-module-import.md @@ -0,0 +1,242 @@ +| Title | module.import(path):Promise | +|--------|-----------------------------| +| Author | @WebReflection | +| Status | DRAFT | +| Date | 2017-01-23 | + +# Asynchronous `module.import(path)` +There is [a standard effort](https://github.com/tc39/proposal-dynamic-import) +to bring asynchronous module loading to JavaScript. + +Such effort will take some time before it will be fully agreed, +and before it will be widely available across all JS engines. + +In the meanwhile, the community could benefit from this _opt-in_ +proposal, which is 100% backward compatible, as well as future friendly, +being based entirely on what's been agreed already by TC39. + +## The pseudo implementation example +The following code, which works already, +represents how essential is this additional +`module.import` method proposal: +```js +// one added method to Module.prototype +module.constructor.prototype.import = + function (path) { + return new Promise( + res => res(this.require(path)) + ); + }; +``` + +### Fully backward compatible +Every existent NodeJS/CommonJS module can be imported already. +There is no need to change anything to the existing code-base. +```js +// even core modules work already +module.import('fs').then(fs => { + // do something with fs module + fs.readdir('.', console.log); +}); +``` + +### Multiple imports too +Keeping the implementation as simple as possible in order +to have zero conflicts with current dynamic +standard `import` proposal, we can use standard +`Promise.all` method to retrieve at once every needed module. +```js +Promise.all([ + module.import('fs'), + module.import('path') +]).then(([fs, path]) => { + fs.readdir( + path.join(__dirname, 'test.js'), + console.log + ); +}); +``` + +It's eventually possible to shortcut multiple imports +using the following pattern. +However, it won't be fully compatible +with current standard `import` proposal as it is today. +```js +Promise.all([ + 'fs', + 'path' +].map( + m => module.import(m) +)).then(modules => {}); +``` + +## Unleashing asynchronous exports +While good old modules will keep being usable, +new modules might define themselves as asynchronous +by exporting a `Promise` rather than directly the module. +```js +// example: dir.js +module.exports = Promise.all([ + module.import('fs'), + module.import('path') +]).then(([fs, path]) => { + // once all dependencies have been imported + // return the module that should be imported + return { + list(dir) { + return new Promise((res, rej) => { + fs.readdir( + path.resolve(dir), + (err, list) => { + if (err) rej(err); + else res(list); + } + ); + }); + } + }; +}); + +// import example +module.import('./dir').then(dir => { + dir.list('.').then(console.log); +}); +``` + +## Asynchronous exports: why? +The example with core modules is mostly for +copy and paste testability sake only, +the truth is that many modules have +asynchronous initialization behind databases connections, +remote API calls, and other non blocking operations. + +New modules created with async `import(...)` in mind +can early reject when needed, instead of emitting +or throwing unhandled errors in the wild at distance. + +Importing a list of 20 modules at once, would be +easy to _catch_ all at once and react accordingly, +as opposite of handling every single module possible +non blocking initialization a part. + +On top of that, on the client side, +every module would most likely be preferred as asynchronous, +so that this proposal can improve Universal-JS-ability even more. + +## Why not as global function ? +The `import` word is reserved. +Unless the community wants to wait until the official standard +is finalized and the v8 change shipped, +using a `global.import(path)` does not seem even +aligned with the concept of module, which should never even use +`global` reference if not to quickly feature test something, +and does not seem to be universally friendly, +since `global` is not standard outside NodeJS world. + +## Why polluting `module` and not `require` ? +There are at least two reasons: + + 1. the Web has a role too + 2. historical confusion and semantics + +The current proposal is aligned with what +`