We use cookies to enhance your experience on the site
CodeWorlds

ES2024/ES2025 Features

JavaScript evolves rapidly! Let's explore the latest language features introduced in ES2024 and ES2025 that make working with our Jurassic Park systems even more elegant.

Object.groupBy and Map.groupBy

Group elements by a criterion — no more manual reduce() for grouping!

1const dinosaurs = [
2  { name: "Rexy", species: "Tyrannosaurus", diet: "carnivore", sector: "A" },
3  { name: "Blue", species: "Velociraptor", diet: "carnivore", sector: "B" },
4  { name: "Spike", species: "Triceratops", diet: "herbivore", sector: "C" },
5  { name: "Sarah", species: "Triceratops", diet: "herbivore", sector: "A" },
6  { name: "Delta", species: "Velociraptor", diet: "carnivore", sector: "B" }
7];
8
9// Group by diet — returns a plain object
10const byDiet = Object.groupBy(dinosaurs, dino => dino.diet);
11console.log(byDiet.carnivore.map(d => d.name)); // ["Rexy", "Blue", "Delta"]
12console.log(byDiet.herbivore.map(d => d.name)); // ["Spike", "Sarah"]
13
14// Group by sector — returns a Map
15const bySector = Map.groupBy(dinosaurs, dino => dino.sector);
16console.log(bySector.get("A").map(d => d.name)); // ["Rexy", "Sarah"]
17console.log(bySector.get("B").map(d => d.name)); // ["Blue", "Delta"]

Array Methods: toSorted, toReversed, toSpliced, with

New non-mutating array methods that return a new array instead of modifying the original:

1const dinoNames = ["Rexy", "Blue", "Spike", "Delta", "Sarah"];
2
3// toSorted() — non-mutating sort
4const sorted = dinoNames.toSorted();
5console.log(sorted);   // ["Blue", "Delta", "Rexy", "Sarah", "Spike"]
6console.log(dinoNames); // ["Rexy", "Blue", "Spike", "Delta", "Sarah"] — unchanged!
7
8// Compare with old sort():
9const old = [...dinoNames].sort(); // Had to copy first
10
11// toReversed() — non-mutating reverse
12const reversed = dinoNames.toReversed();
13console.log(reversed);  // ["Sarah", "Delta", "Spike", "Blue", "Rexy"]
14
15// toSpliced() — non-mutating splice
16const withNew = dinoNames.toSpliced(2, 1, "Triki", "Bronto");
17console.log(withNew);    // ["Rexy", "Blue", "Triki", "Bronto", "Delta", "Sarah"]
18console.log(dinoNames);  // unchanged
19
20// with() — replace a single element by index
21const updated = dinoNames.with(0, "T-Rex Alpha");
22console.log(updated);    // ["T-Rex Alpha", "Blue", "Spike", "Delta", "Sarah"]
23console.log(dinoNames);  // unchanged

Promise.withResolvers

Exposes the resolve/reject functions outside the Promise constructor:

1// Old way — resolver/rejecter captured via closure
2let resolve, reject;
3const promise = new Promise((res, rej) => {
4  resolve = res;
5  reject = rej;
6});
7
8// New way — cleaner!
9const { promise, resolve, reject } = Promise.withResolvers();
10
11// Useful for creating controllable async operations
12class DinosaurSensor {
13  constructor(id) {
14    this.id = id;
15    const { promise, resolve, reject } = Promise.withResolvers();
16    this.readingPromise = promise;
17    this.resolveReading = resolve;
18    this.rejectReading = reject;
19  }
20
21  receiveReading(data) {
22    this.resolveReading(data);
23  }
24
25  sensorFailure(error) {
26    this.rejectReading(error);
27  }
28}
29
30const sensor = new DinosaurSensor("TREX-001");
31
32// Set up what happens when reading arrives
33sensor.readingPromise.then(reading => {
34  console.log(`Sensor ${sensor.id}: ${JSON.stringify(reading)}`);
35});
36
37// Later, when the reading arrives:
38sensor.receiveReading({ temperature: 38.5, heartRate: 72 });

Set Methods (ES2024)

New Set operations for mathematical set algebra:

1const carnivores = new Set(["T-Rex", "Velociraptor", "Spinosaurus"]);
2const largeDinos = new Set(["T-Rex", "Brachiosaurus", "Spinosaurus"]);
3const parkDinos = new Set(["T-Rex", "Velociraptor", "Triceratops"]);
4
5// intersection() — elements in both sets
6const largeCarnivores = carnivores.intersection(largeDinos);
7console.log([...largeCarnivores]); // ["T-Rex", "Spinosaurus"]
8
9// difference() — in first but not second
10const smallCarnivores = carnivores.difference(largeDinos);
11console.log([...smallCarnivores]); // ["Velociraptor"]
12
13// union() — all elements from both
14const allKnown = carnivores.union(largeDinos);
15console.log([...allKnown]); // ["T-Rex", "Velociraptor", "Spinosaurus", "Brachiosaurus"]
16
17// symmetricDifference() — in one but not both
18const uniqueToEach = carnivores.symmetricDifference(largeDinos);
19console.log([...uniqueToEach]); // ["Velociraptor", "Brachiosaurus"]
20
21// isSubsetOf() — are all elements of carnivores in parkDinos?
22console.log(new Set(["T-Rex"]).isSubsetOf(parkDinos)); // true
23console.log(carnivores.isSubsetOf(parkDinos)); // false (Velociraptor is there, Spinosaurus not)
24
25// isSupersetOf() — does parkDinos contain all of the subset?
26console.log(parkDinos.isSupersetOf(new Set(["T-Rex", "Triceratops"]))); // true
27
28// isDisjointFrom() — no elements in common?
29const oceanDinos = new Set(["Mosasaurus", "Plesiosaurus"]);
30console.log(carnivores.isDisjointFrom(oceanDinos)); // true

Promise.try (ES2025)

Wraps both synchronous and asynchronous functions uniformly:

1// Without Promise.try — must handle sync/async differently
2function getDinosaurStatus(id) {
3  return new Promise((resolve, reject) => {
4    try {
5      const result = syncOperation(id); // might throw synchronously
6      resolve(asyncOperation(result));  // might reject
7    } catch (e) {
8      reject(e);
9    }
10  });
11}
12
13// With Promise.try — cleaner
14function getDinosaurStatus(id) {
15  return Promise.try(() => {
16    const result = syncOperation(id);  // sync throws are caught
17    return asyncOperation(result);     // async rejections are caught
18  });
19}
20
21// Practical: safely execute any function as a promise
22const safeCheck = Promise.try(() => {
23  validateDinoId("RAPTOR-001"); // might throw
24  return fetchDinosaurData("RAPTOR-001"); // returns promise
25});
26
27safeCheck
28  .then(data => console.log("Data:", data))
29  .catch(err => console.error("Error:", err));

Import Attributes (ES2024)

1// Import JSON directly
2import config from './config.json' with { type: 'json' };
3import translations from './i18n/en.json' with { type: 'json' };
4
5// Dynamic import with attributes
6const data = await import('./park-data.json', { with: { type: 'json' } });
7
8console.log(config.parkName); // "Jurassic Park"
9console.log(translations.welcome); // "Welcome to Jurassic Park!"

Summary

The latest JavaScript features make code more expressive and reduce boilerplate:

  1. Object.groupBy / Map.groupBy — clean array grouping without reduce()
  2. toSorted, toReversed, toSpliced, with — immutable array operations
  3. Promise.withResolvers — expose resolve/reject outside the constructor
  4. Set methods — intersection, difference, union, symmetric difference
  5. Promise.try — uniform handling of sync and async code
  6. Import Attributes — type-safe, explicit module type declarations

These features reflect modern JavaScript's move toward more functional, immutable, and ergonomic patterns — making even the most complex Jurassic Park systems easier to write and maintain!

Go to CodeWorlds