Business
Jobs
  • About Us
  • Solutions
    • Job Postings
      Post your job and receive qualified candidates in 48h.
    • Candidate Assessments
      500+ technical and psychological tests, plus anti-fraud.
    • Headhunting
      Tailor-made executive search from start to finish.
    • Payroll + EOR
      Payroll dispersal and EOR across 15+ LATAM countries.
  • Pricing
  • Jobs

0

247
Views
When named ES6 exports are asynchronously mutated, why is the change picked up by importers?

Consider the following example:

// module.mjs

export let member = "initial"

setTimeout(() => { 
  member = "mutated" 
}, 2000)
// index.mjs

import { member } from "./module.mjs"

setInterval(() => { 
  console.log(member) 
}, 300);

If you run index.mjs it prints "initial" a few times and after the 2-second timeout starts printing "mutated".

This is surprising to me. I expected it to just keep printing "initial" since the reference to member has changed. I wouldn't be surprised if my index.mjs looked like this

// index.mjs

import * as allExports from "./module.mjs"

setInterval(() => { 
  console.log(allExports.member) 
}, 300);

The reference to allExports hasn't changed. So by accessing member as a property I have access to the new reference. And fair enough, this produces the same result.

But why does it also work in the first case? Are import references updated every time the context for a function call is constructed? Well, then it should also work with default-exports. However, after adjusting the code like this:

// module.mjs

let member = "initial"

export default member

setTimeout(() => { 
  member = "mutated" 
}, 2000)
// index.mjs

import member from "./module.mjs"

setInterval(() => { 
  console.log(member) 
}, 300);

index.mjs just keeps printing "initial" according to my intuition. Hmm, maybe there is a fundamental difference between default-exports and named-exports. What if I do this:

// module.mjs

export let member = "initial"

setTimeout(() => { 
  member = "mutated" 
}, 2000)
// index.mjs

import * as allExports from "./module.mjs"

let { member } = allExports

setInterval(() => { 
  console.log(member) 
}, 300);

Wow, it keeps printing "initial". This is where I got really confused. I always assumed that

import { member } from "./module.mjs"

is just short for importing all named-exports as an object and then doing destructuring.

So my question is: what underlying principle justifies this unique behavior of named-exports?

about 4 years ago · Juan Pablo Isaza
1 answers
Answer question

0

The reason for this is because technically ES6 modules export and import bindings and not values. When you do:

export let test = 0

You need to think that you are exporting test not 0. That means any change on the variable will be reflected on their imports.

The reason this keeps printing initial:

// index.mjs

import * as allExports from "./module.mjs"

let { member } = allExports

setInterval(() => { 
  console.log(member) 
}, 300);

Is because allExports is a special object. You are creating a new binding here and the value gets copied but you are losing the imported binding.

about 4 years ago · Juan Pablo Isaza Report
Answer question
Find remote jobs

Discover the new way to find a job!

Top jobs
Top job categories
Business
Post vacancy Pricing Sales
Legal
Terms and conditions Privacy policy
© 2026 PeakU Inc. All Rights Reserved.
Andres GPT
Show me some job opportunities
There's an error!