mde / ejs

Embedded JavaScript templates -- http://ejs.co
Apache License 2.0
7.7k stars 841 forks source link

Add the possibility to asynchroniously obtain templates #708

Open N247S opened 1 year ago

N247S commented 1 year ago

Hello everyone,

I use ejs for both server and client side rendering. But I found out that the clientside functions (eventhough templates can be compiled with async: true) are not really able to handle asynchronous 'include' functions.

So there are a few options to modify the behavior the 'include' function to make it situation-fit.

  1. On the server side there is ejs.fileLoader which allows for the actual file-reading to be modified. This won't work because on the client-side there is no access to any fileSystem (used prior to the ejs.fileLoader.

  2. There is a includer property in ejs.Options, but it is poorly documented and not clearly referenced in the ejs.js file. So I am not sure if it is usable (I assume only server-side as the passed arguments are already refactured paths).

  3. There is a third argument for the ejs.AsyncClientFunction and ejs.ClientFunction which can be used client-side for polyfilling the include function.

Only the third option is useable on the clientside as far as I gathered, but its function does not allow asynchronous actions which can be really bad for performance and usability reasons.

In my case I load a subset of templates from the server and use them clientside. I would be able to do so syncronously with an XMLHttpRequest with the async flag set to false, but that obviously would be a verry bad idea. I am pretty sure this approuch wouldn't be usable when working with Workers either.

In any case there would be a huge benefit to be able to use asyncrhonous functions. I am not sure how/where it is implemented, otherwise I would have tried to create a PR, But the following snippet would allow both sync and async functions to be used as includer.

// suppose something like this would be used to obtain an `include` statement
let includedString = includer(path, data);
// easily refactured to allow both sync and async versions of `includer`, assuming the context is async as well!
let includedString = await Promise.resolve(includer(path. data));

Would love to hear your feedback on this, or any suggestions on how to temp-fix this issue in the meantime!