Guide on exporting values from a Promise within an imported module

Recently, I encountered a challenge where I needed to integrate a pure ESM package into a non-module. Unfortunately, modifying the script to accommodate this requirement was not an option.

To tackle this issue, I turned to using the import() function (also known as a "dynamic import"). This method returns a Promise instead of the actual module, and in the absence of being able to use await due to the non-module context, I resorted to utilizing .then().

The pure ESM package in question (unist-util-visit) is utilized within a function exported by my script, which is further employed in another script. To provide some clarity, the import hierarchy goes as follows:

importer.js imports imported.js imports unist-util-visit

A perplexing problem emerged wherein any exports from within the .then() block in imported.js failed to manifest in importer.js.

This predicament persisted even after taking timing into account. In fact, I resorted to leveraging an EventEmitter to ensure that importer.js waited until the execution of imported.js's .then() block was completed:

imported.js:

const EventEmitter = require('events');

module.exports.emitter = new EventEmitter();
module.exports.outsideFxn = function () {
  console.log('hello');
}
import('unist-util-visit').then((unistUtilVisit) => {
  module.exports.fxn = function() {
    console.log(`unistUtilVisit: ${typeof unistUtilVisit}`);
  }
  module.exports.emitter.emit('ready');
});

importer.js:

import('./imported.js').then((imported) => {
  console.log("In importer.js's .then():");
  console.log('  fxn:', imported.fxn);
  console.log('  outsideFxn:', imported.outsideFxn);
  imported.emitter.on('ready', () => {
    console.log("After imported.js is done:")
    console.log('  fxn:', imported.fxn);
  });
});

Upon execution, the output observed was:

$ node importer.js 
In importer.js's .then():
  fxn: undefined
  outsideFxn: [Function (anonymous)]
After imported.js is done:
  fxn: undefined

How could this be resolved? What crucial aspect am I overlooking that prevents the proper definition of exports within the .then() function? Is there a way for me to successfully export my function?

Answer №1

Why not change your approach from modifying the module's exports after it has been consumed by dependents, to exporting a promise that returns the function once it is ready?

module.exports.fxnP = 
    import('unist-util-visit')
      .then((unistUtilVisit) => () => { 
          console.log(`unistUtilVisit: ${typeof unistUtilVisit}`); 
      });

This way, you can consume it like this:

import('./imported.js').then((imported) => {
    imported.fxnP.then((fxn) => {
        fxn();
    });
});

Alternatively, you could do it more elegantly:

import('./imported.js')
    .then(({fxnP}) => fxnP)
    .then((fxn) => fxn());

Answer №2

If you're looking for a way to achieve your desired outcome synchronously, consider using a specialized require hook such as jiti.

Here's how you can implement this without hooking require:

$ node test.cjs

// test.cjs
const jiti = require('jiti')();
const unistUtilVisit = jiti("unist-util-visit");

To programmatically hook require, follow these steps:

$ node test.cjs

// test.cjs
require('jiti')().register();
const unistUtilVisit = require("unist-util-visit");

You can also use a command line option to hook require:

$ node -r jiti/register test.cjs

// test.cjs
const unistUtilVisit = require("unist-util-visit");

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

Unable to display script in the sources tab on NW.js 16

After updating to the latest version of NW.js (nwjs-sdk-v0.16.1-linux-x64) and attempting to migrate my project over, I encountered a strange issue with debugging. The script I was working on, named "bunny.js", would show up under "Applications" but not in ...

"Rearranging" messages once they have been processed in a different order

In the process of developing a highly-available distributed message-passing system, we are faced with the challenge of ensuring that all messages sent to a specific destination are processed in order while maintaining efficiency. Currently, processing mess ...

Implementing a new archival capability on my website

I'm implementing a feature on my website that allows users to archive posts as needed. The site is developed using MERN stack and a RESTful API. Initially, I considered updating the Post model with an 'archived' property like: { archived: ...

The PKIJS digital signature does not align with the verification process

Explore the code snippet below const data = await Deno.readFile("./README.md"); const certificate = (await loadPEM("./playground/domain.pem"))[0] as Certificate; const privateKey = (await loadPEM("./playground/domain-pk ...

What could be causing the second image to not drop in the proper position in an HTML and JavaScript environment?

I am encountering an issue with a simple drag and drop implementation using images in my code. The first image works fine, but for some reason the second image does not display correctly when dragged inside the div boxes; it appears outside of the box. Can ...

What is the process for retrieving a bad port (6666) in a Node.js application?

Currently, in node.js version v18.14.0, my task involves fetching data from port 6666 (the URL is just a placeholder in this instance): await fetch("http://placeholder.com:6666/secret-service", {"method": "GET"}); Upon attem ...

cross-domain ajax response

Imagine a unique scenario that will pique the interest of many developers. You have a JavaScript file and a PHP file - in the JS file, you've coded AJAX to send parameters via HTTP request to the PHP file and receive a response. Now, let's delve ...

Bring in items and then go through each one

I'm curious if there's a way to loop through imported objects? import { Row, Col, Form, FormItem, Icon, Input, Tooltip, Image, Button, Dialog } from 'element-ui' objects.forEach(object => { // do something here }) When I have a ...

Load suggestions from Material UI AutoComplete dynamically based on user input

I am facing a challenge with an Autocomplete component that needs to handle a large data list, up to 6000 elements, and display suggestions based on user input. Due to the sheer number of options, when a user types on a slower computer, there is a noticeab ...

Is there a way to automatically dismiss a notify.js notification?

I am currently attempting to forcefully close an opened notification by clicking a button, following the instructions provided in the advanced example of the notify.js API. Can someone please explain how this can be accomplished? function CLOSE() { $( ...

Visit a webpage on my site

Is it feasible to embed a website within another website? Suppose I have my index.html file. What if in the center of that file, we include an iFrame or similar element that loads , allowing users to explore Google directly on my website? ...

NodeJS not able to properly deliver the index.html file as a response

app.get(`/`, (req, res, next) => { console.log(`The / get route is being accessed`); const indexHtml = path.resolve( __dirname + '/../public/index.html' ); res.sendFile(indexHtml); next(); }); I've been strugg ...

Obtain the form value upon submission without the need to reload the page

I am facing an issue where I have a form and I want to trigger the display of a div (in the same page) and execute a JavaScript function upon clicking submit, all without causing a page refresh. <form action="#" method="post" name= "form1" id = "form ...

Uploading Files to Amazon S3 Using Client-Side Technology

Seeking guidance on how to enable users of my NodeJS/Express webapp, hosted on AWS Elastic Beanstalk, to upload profile pictures directly to AWS S3. I prefer direct uploading to S3 as I intend to support larger file uploads in the future. Though inexperi ...

Serve up a 400 error response via the express server when hitting a

I need help serving a 400 error for specific files within the /assets directory that contain .map in their names. For example: /assets/foo-huh4hv45gvfcdfg.map.js. Here's the code I tried, but it didn't work as expected: app.get('/assets&bs ...

Exploring advanced slot functionality in VuetifyJS through autocomplete integration with Google Places API

How can I make VuetifyJS advanced slots work seamlessly with the Google Places API? Currently, some addresses appear in the autocomplete dropdown only after clearing the input text by clicking the "x" icon in the form field. To see the problem in action, ...

Set a restriction on the Bootstrap DatePicker to only show dates within a

My application features StartDate and EndDate datepickers, and I need to implement a 30-day limit on the selection range to prevent performance issues caused by loading too much data. I'm looking for a functionality where if the user picks today as t ...

Can Socket.io support the use of both flash sockets and websockets at the same time?

I have been grappling with this issue for several days now, searching for answers far and wide without success. In essence, I am dealing with a flash client app and a browser app based on Chrome connected to a socket io instance powered by nodejs. Chrome ...

Is there an error when iterating through each table row and extracting the values in the rows?

Here is a basic table that I am attempting to iterate through in order to retrieve the value of each cell in every row where there are <td>s present. However, I encounter an error indicating that find does not exist despite having added jQuery. Any ...

Recoil: Executing a function when an atom is modified

My goal is to store a user object in an atom and cache it in localStorage every time it changes to avoid having the user sign in repeatedly if the app crashes: localStorage.setItem('user', JSON.stringify(user)) Previously, with useContext, I ach ...