Categories
JavaScript

New JavaScript Features Coming in ES2020 That You Can Use Now

Since the release of ES6 in 2015, JavaScript has been evolving fast with tons of new features coming out in each iteration. The new versions of the JavaScript language specification have been updated yearly, with new language feature proposal being finalized faster than ever. This means that new features are getting incorporated into modern browsers and other JavaScript run-time engines, like Node.js, at a pace that we haven’t seen before.

In 2019, there are many new features that are in the ‘Stage 3’ phase, which means that it’s very close to being finalized, and browsers/Node are getting support for these features now. If we want to use them for production, we can use something like Babel to transpile them to older versions of JavaScript so they can be used in older browsers like Internet Explorer if needed.

In this article, we look at private fields in classes, optional chaining, nullish coalescing operator, and BigInts.

Private Fields in Classes

One of the latest proposals is a way to add private variables in classes. We will use the # sign to indicate that a class variable is a private variable. This way, we don’t need to use closures to hide private variables that we don’t want to expose to the outside world. For example, we can write a simple class to increment and decrement numbers like the following code:

class Counter {
  #x = 0;

  increment() {
    this.#x++;
  }

  decrement() {
    this.#x--;
  }

  getNum(){
    return this.#x;
  }
}

const c = new Counter();
c.increment();
c.increment();
c.decrement();
console.log(c.getNum());

We should get the value 1 from the console.log in the last line of the code above. #x is a private variable that can’t be accessed outside the class. So if we write:

console.log(c.#x);

We’ll get Uncaught SyntaxError: Private field '#x'.

Private variables are a much-needed feature of JavaScript classes. This feature is now in the latest version of Chrome and Node.js v12.

Optional Chaining Operator

Currently, if we want to access a deeply nested property of an object, we have to check if the property in each nesting level is defined by using long boolean expressions. For example, we have to check each property is defined in each level until we can access the deeply nested property that we want, as in the following code:

const obj = {
  prop1: {
    prop2: {
      prop3: {
        prop4: {
          prop5: 5
        }
      }
    }
  }
}

obj.prop1 &&
  obj.prop1.prop2 &&
  obj.prop1.prop2 &&
  obj.prop1.prop2.prop3 &&
  obj.prop1.prop2.prop3.prop4 &&
  console.log(obj.prop1.prop2.prop3.prop4.prop5);

Fortunately, the code above has every property defined in every level that we want to access, so we will actually get the value 5. However, if we have a nested object inside an object that is undefined or null in any level, then our program will crash and the rest won’t run if we don’t do a check for it. This means that we have to check every level to make sure that it won’t crash when it runs into a undefined or null object.

With the optional chaining operator, we just need to use the ?. to access nested objects. And if it bumps into a property that’s null or undefined, then it just returns undefined. With optional chaining, we can instead write:

const obj = {
  prop1: {
    prop2: {
      prop3: {
        prop4: {
          prop5: 5
        }
      }
    }
  }
}

console.log(obj?.prop1?.prop2?.prop3?.prop4?.prop5);

When our program runs into an undefined or null property, instead of crashing, we’ll just get back undefined. Unfortunately, this hasn’t been natively implemented in any browser, so we have use the latest version Babel to use this feature.

Nullish Coalescing Operator

Another issue that comes from null or undefined values is that we have to make a default value to set a variable in case the variable that we want is null or undefined. For example, if we have:

const y = x || 500;

Then we have to make a default value in case x is undefined when it’s being set to y by using the || operator. The problem with the || operator is that all falsy values like 0, false, or an empty string will be overridden with the default value which we don’t always want to do.

To solve this, there was a proposal to create a “nullish” coalescing operator, which is denoted by ??. With it, we only set the default value if the value if the first item is either null or undefined. For example, with the nullish coalescing operator, the expression above will become:

const y = x ?? 500;

For example, if we have the following code:

const x = null;
const y = x ?? 500;
console.log(y); // 500

const n = 0
const m = n ?? 9000;
console.log(m) // 0

y will be assigned the value 500 since x has the value null. However, m will be assigned the value 0 since n is not null or undfined. If we had used || instead of ??, then m would have been assigned the value 9000 since 0 is falsy.

Unfortunately, this feature isn’t in any browser or Node.js yet so we have to use the latest version of Babel to use this feature.

BigInt

To represent integers larger than 2 to the 53rd power minus 1 in JavaScript, we can use the BigInt object. It can be manipulated via normal operations like arithmetic operators — addition, subtraction, multiplication, division, remainder, and exponentiation. It can be constructed from numbers and hexadecimal or binary strings. Also, it supports bitwise operations like AND, OR, NOT, and XOR. The only bitwise operation that doesn’t work is the zero-fill right shift operator (>>>) because BigInts are all signed.

Also the unary + operator isn’t supported for adding Numbers and BitInts. These operations only are done when all the operands are BigInts. In JavaScript, a BigInt is not the same as a normal number. It’s distinguished from a normal number by having an n at the end of the number.

We can define a BigInt with the BigInt factory function. It takes one argument that can be an integer number or a string representing a decimal integer, hexadecimal string, or binary string. BigInt cannot be used with the built-in Math object. Also, when converting between numbers and BigInt and vice versa, we have to be careful because the precision of BigInt might be lost when a BigInt is converted into a number.

To define a BigInt, we can write the following if we want to pass in a whole number:

const bigInt = BigInt(1);
console.log(bigInt);

Then it would log 1n when we run the console.log statement. If we want to pass in a string into the factory function instead, we can write:

const bigInt = BigInt('2222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222');
console.log(bigInt);

We can also pass in a hexadecimal number string by using a string that starts with 0x into the factory function:

const bigHex = BigInt("0x1fffffffffffff111111111");
console.log(bigHex);

The code above would log 618970019642690073311383825n when we run console.log on bigHex.

Likewise, we can pass in a binary string with a string that starts with 0b to get a BigInt:

const bigBin = BigInt("0b111111111111111000000000011111111111111111111111");
console.log(bigBin);

The code above would get us 281466395164671n when we run console.log on it. Passing in strings would be handy if the BigInt that we want to create is outside of the range that can be accepted by the number type.

We can also define a BigInt with a BigInt literal. We can just attach an n character to the end of a whole number. For example, we can write:

const bigInt = 22222222222222222222222222222222n;
console.log(bigInt);

Then we get 22222222222222222222222222222222n as the value of bigInt when we log the value. We can do arithmetic operations like +, -, /, *, % with BigInts like we do with numbers. All operands have to be BigInts if we want to do these operations with BigInts. However, if we get fractional results, the fractional parts will be truncated. BigInt is a big integer and it’s not for storing decimals. For example, in the examples below:

const expected = 8n / 2n;
console.log(expected) // 4n

const rounded = 9n / 2n;
console.log(rounded) // 4n

We get 4n for expected and rounded. This is because the fractional part is removed from the BigInt.

Comparison operators can be applied to BigInts. The operands can be either BigInt or numbers. For example, we can compare 1n to 1:

1n === 1

The code above would evaluate to false because BigInt and number aren’t the same type. But when we replace triple equals with double equals, like in the code below:

1n == 1

The statement above evaluate to true because only the value is compared. Note that in both examples, we mixed BigInt operands with number operands. This is allowed for comparison operators. BigInts and numbers can be compared together with other operators as well, like in the following examples:

1n < 9
// true

9n > 1
// true

9 > 9n
// false

9n > 9
// false

9n >= 9
// true

Also, they can be mixed together in one array and sorted. For example, if we have the following code:

const mixedNums = [5n, 6, -120n, 12, 24, 0, 0n];
mixedNums.sort();
console.log(mixedNums)

This feature works with the latest version of Chrome and Node.js now, so we can start using it in our apps. We can use Babel to make it work with older browsers.

Conclusion

We can use the # sign to indicate that a class variable is a private variable. This way, we don’t have to use closures to hide private variables that we don’t want to expose to the outside world. To solve problems with null and undefined values in objects, we have the optional chaining operator to access properties without checking that each level might be null or undefined. With the nullish coalescing operator, we can set default values for variables only for cases when it’s null or undefined. With BigInt objects, we can represent big numbers outside of the safe range of regular numbers in JavaScript and do the standard operations with them, except that fractional parts will be omitted from results.

Categories
JavaScript Answers

New JavaScript Features Coming in ES2020 That You Can Use Now (Part 2)

Since 2015, the JavaScript is evolving fast with lots of new features coming out in each iteration. New versions of the JavaScript language specification have been updated yearly, with new language feature proposals being finalized faster than ever.

This means that new features are getting incorporated into modern browsers and other JavaScript run-time engines like Node.js at a pace that we haven’t seen before. In 2019, there are many new features that are in the ‘Stage 3’ phase, which means that it’s very close to being finalized and browsers are getting support for these features now.

If we want to use them for production code, we can use something like Babel to transpile them to older versions of JavaScript so they can be used in older browsers like Internet Explorer if needed.

In this article, we’ll look at static fields and methods in classes and using the await keyword at the top level in regular code and module exports.

Static Fields and Methods in Classes

Static fields in classes is a new feature that’s coming soon to browsers and Node.js. It lets us add static fields for classes that do not need an instance of the class to be created to be accessed. They can both be private or public. It’s a handy replacement for enums. For example, with the latest proposal, we can write a class with static variables like in the following example:

class Fruit {
  static orange = 'orange';
  static grape = 'grape';
  static banana = 'banana';
  static apple = 'apple';

  static #secretPear = 'pear';

}

console.log(Fruit.orange);
console.log(Fruit.grape);
console.log(Fruit.banana);
console.log(Fruit.apple);

The example above should output:

orange
grape
banana
apple

If we try to access secretPear from outside the class, like in the following code:

console.log(Fruit.#secretPear);

We’ll get ‘Uncaught SyntaxError: Private field ‘#secretPear’ must be declared in an enclosing class.’

The keyword static is already working with methods in the classes since ES6. For example, we can declare a static method within a class by using the static keyword like in the following code:

class ClassStatic {
  static staticMethod() {
    return 'Static method has been called.';
  }
}

console.log(ClassStatic.staticMethod());

We should get ‘Static method has been called’ from the console.log output. As we can see the staticMethod method was called without instantiating the class ClassStatic class just like we expected. Static methods can also be private, we just add a # sign before the name of the method to make it private. For example, we can write the following code:

class ClassStatic {
  static #privateStaticMethod(){
    return 'Static method has been called.';
  }

  static staticMethod() {
    return ClassStatic.#privateStaticMethod();
  }
}

console.log(ClassStatic.staticMethod());

In the code above, we called the privateStaticMethod from the staticMethod to get the value of the return value from privateStaticMethod in staticMethod. Then when we call staticMethod by running ClassStatic.staticMethod() then we get ‘Static method has been called.’ However, we can’t call privateStaticMethod directly from outside the class, so if we run something like:

class ClassStatic {
  static #privateStaticMethod(){
    return 'Static method has been called.';
  }

  static staticMethod() {
    return ClassStatic.#privateStaticMethod();
  }
}

console.log(ClassStatic.#privateStaticMethod());

Then we will get an error. Also, note that we aren’t using this to access the privateStaticMethod , we used the class name ClassStatic to do it. If we use this to call it, as in this.#privateStaticMethod() , we’ll get an error or undefined depending on the Babel version we’re using. The static keyword for variables is partially supported within browsers. Chromium browsers like Chrome and Opera support it. Also, Node.js supports this keyword. Other browsers do not support it yet, so we’ve to use Babel to convert it into older JavaScript code for it to run on other browsers.

Top Level await

Currently, the await keyword can only be used inside async functions, which are functions that return promises. If we use the await keyword outside of an async function, we’ll get an error like ‘Cannot use keyword ‘await’ outside an async function’ and our program won’t run. Now we can use the await keyword without it being inside an async function. For example, we can use it with the following code:

const promise1 = new Promise((resolve)=>{
  setTimeout(()=> resolve('promise1 resolved'), 1000);
})

const promise2 = new Promise((resolve)=>{
  setTimeout(()=> resolve('promise2 resolved'), 1000);
})

const val1 = await promise1;
console.log(val1);
const val2 =  await promise2;
console.log(val2);

Then when we run this code above in Chrome, we’ll get:

promise1 resolved
promise2 resolved

With top-level await, we can run asynchronous code however we like, and we don’t have to go back to using the long-winded then function chaining with callbacks passed into each then function call. This is very clean and we don’t have to wrap it inside an async IIFE (immediately invoked function expression) to run it if we don’t want to declare a named function.

Another good thing about top-level await is that we can export it directly. This means that when the module exports something that has the await keyword, it will wait for the promise being the await keyword to resolve before running anything. On the surface, it acts as if it’s imported synchronously as usual, but underneath, it actually waits for the promise in the export expression to resolve. For example, we can write:

const promise1 = new Promise((resolve)=>{
  setTimeout(()=> resolve('promise1 resolved'), 1000);
})
export const val1 = await promise1;

When we import module1.js in module2.js :

import { val1 } from './module1';
console.log(`val1);

Then we can see the value of val1 logged as if it’s running synchronously, waiting for promise1 to resolve.

Conclusion

Static fields in classes is a new feature that’s coming soon to browsers and Node.js. It lets us add static fields for classes that do not need an instance of the class to be created to be accessed. They can both be private or public. It’s a handy replacement for enums.

Static methods have existed since ES6 and so it can be used now and has widespread support. Using the await keyword at the top level in regular code and module exports is very handy once it’s a finalized feature. It lets us use await to write asynchronous code outside of async functions, which is much cleaner than using then with callbacks.

Also, it can let us export the resolved value of asynchronous code and then import it and it use it in other modules as if it was synchronous code.

Categories
Nodejs

Using Events in Node.js

The core feature of Node.js is asynchronous programming. This means that code in Node.js may not be executed sequentially. Therefore, data may not be determined in a fixed amount of time. This means that to get all the data we need, we have to pass data around the app when the data is obtained. This can be done by emitting, listening to, and handling events in a Node.js app. When an event with a given name is emitted, the event can listen to the listener, if the listener is specified to listen to the event with the name. Evenemitter functions are called synchronously. The event listener code is a callback function that takes a parameter for the data and handles it. Node.js has an EventEmitter class — it can be extended by a new class created to emit events that can be listened to by event listeners.

Define Event Emitters

Here’s a simple example of creating and using the EventEmitter class:

const EventEmitter = require('events');

class Emitter extends EventEmitter {}

const eventEmitter = new Emitter();
eventEmitter.on('event', () => {
  console.log('event emitted!');
});

eventEmitter.emit('event');

We should get ‘event emitted!’ in the console log. In the code above, we created the Emitter which extends the EventEmitter class, which has the emit function we called in the last line. The argument of the emit function is the name of the event, which we listen to in this block of code:

eventEmitter.on('event', () => {
  console.log('event emitted!');
});

The callback function, after the 'event' argument above, is the event handler function, that runs when the event is received.

In the code above, we emitted an event. However, it’s not very useful since we didn’t pass any data with the emitted event when we emit the event so it doesn’t do much. Therefore, we want to send data with the event so that we can pass data around so that we can do something useful in the event listener. To pass data when we emit an event, we can pass in extra arguments after the first argument, which is the event name. For instance, we can write the following code:

const EventEmitter = require('events');
class Emitter extends EventEmitter {}
const eventEmitter = new Emitter();

eventEmitter.on('event', (a, b) => {
  console.log(a, b);
});

eventEmitter.emit('event', 'a', 'b');

If we run the code above, we get ‘a’ and ‘b’ in the console.log statement insider the event handler callback function. As we see, we can pass in multiple arguments with the emit function to pass data into event handlers that subscribe to the event. After the first one, the arguments in the emit function call are all passed into the event listener’s callback function as parameters, so they can be accessed within the event listener callback function.

We can also access the event emitter object inside the event listener callback function. All we have to do is change the arrow function of the callback to a tradition function, as in this code:

const EventEmitter = require('events');
class Emitter extends EventEmitter {}
const eventEmitter = new Emitter();

eventEmitter.on('event', function(a, b){
  console.log(a, b);
  console.log(`Instance of EventEmitter: ${this instanceof EventEmitter}`);
  console.log(`Instance of Emitter: ${this instanceof Emitter}`);
});

eventEmitter.emit('event', 'a', 'b');

If we run the code above, we get this logged in the console.log statements inside the event listener callback function:

a b
Instance of EventEmitter: true
Instance of Emitter: true

On the other hand, if we have the following:

const EventEmitter = require('events');
class Emitter extends EventEmitter {}
const eventEmitter = new Emitter();

eventEmitter.on('event', (a, b) => {
  console.log(a, b);
  console.log(`Instance of EventEmitter: ${this instanceof EventEmitter}`);
  console.log(`Instance of Emitter: ${this instanceof Emitter}`);
});

eventEmitter.emit('event', 'a', 'b');

Then we get this logged in the console.log statements inside the event listener callback function.:

a b
Instance of EventEmitter: false
Instance of Emitter: false

This is because arrow functions do not change the this object inside it. However, tradition functions do change the content of the this object.

EventEmitter calls all listeners synchronously, in the order that they’re registered. This eliminates the chance of race conditions and other logic errors. To handle events asynchronously, we can use the setImmediate() or the process.nextTick() methods:

const EventEmitter = require('events');
class Emitter extends EventEmitter {}
const eventEmitter = new Emitter();

eventEmitter.on('event', (a, b) => {
  setImmediate(() => {
    console.log('event handled asychronously');
  });
});
eventEmitter.emit('event', 'a', 'b');

In the code above, we put the console.log inside a callback function of the setImmediate function, which will run the event handling code asynchronously instead of synchronously.

Events are handled every time they’re emitted. For example, if we have:

const EventEmitter = require('events');
class Emitter extends EventEmitter {}
const eventEmitter = new Emitter();
let x = 1;

eventEmitter.on('event', (a, b) => {
  console.log(x++);
});

for (let i = 0; i < 5; i++){
  eventEmitter.emit('event');
}

Since we emitted the ‘event’ event five times, we get this:

1
2
3
4
5

If we want to emit an event and handle it only the first time it’s emitted, then we use the eventEmitter.once() function, as in this code:

const EventEmitter = require('events');
class Emitter extends EventEmitter {}
const eventEmitter = new Emitter();
let x = 1;

eventEmitter.once('event', (a, b) => {
  console.log(x++);
});

for (let i = 0; i < 5; i++){
  eventEmitter.emit('event');
}

As expected, we only get this logged in the console.log statement of the event handler above:

1

Error Handling

If an error event is emitted in the case of errors, it’s treated as a special case within Node.js. If the EventEmitter doesn’t have at least one error event listener register and an error is emitted, the error is thrown, and the stack trace of the error will be printed, and the process will exit. For example, if we have the following code:

const EventEmitter = require('events');
class Emitter extends EventEmitter {}
const eventEmitter = new Emitter();

eventEmitter.emit('error', new Error('Error occured'));

Then we get something like this and the program exits:

Error [ERR_UNHANDLED_ERROR]: Unhandled error. (Error: Error occured
    at evalmachine.<anonymous>:5:28
    at Script.runInContext (vm.js:133:20)
    at Object.runInContext (vm.js:311:6)
    at evaluate (/run_dir/repl.js:133:14)
    at ReadStream.<anonymous> (/run_dir/repl.js:116:5)
    at ReadStream.emit (events.js:198:13)
    at addChunk (_stream_readable.js:288:12)
    at readableAddChunk (_stream_readable.js:269:11)
    at ReadStream.Readable.push (_stream_readable.js:224:10)
    at lazyFs.read (internal/fs/streams.js:181:12))
    at Emitter.emit (events.js:187:17)
    at evalmachine.<anonymous>:5:14
    at Script.runInContext (vm.js:133:20)
    at Object.runInContext (vm.js:311:6)
    at evaluate (/run_dir/repl.js:133:14)
    at ReadStream.<anonymous> (/run_dir/repl.js:116:5)
    at ReadStream.emit (events.js:198:13)
    at addChunk (_stream_readable.js:288:12)
    at readableAddChunk (_stream_readable.js:269:11)
    at ReadStream.Readable.push (_stream_readable.js:224:10)

To prevent the Node.js program from crashing, we can listen to the error event with a new event listener and handle the error gracefully in the error event handler. For example, we can write:

const EventEmitter = require('events');
class Emitter extends EventEmitter {}
const eventEmitter = new Emitter();

eventEmitter.on('error', (error) => {
  console.log('Error occurred');
});

eventEmitter.emit('error', new Error('Error occurred'));

Then we get “error occurred” logged. We can also get the error content with the error parameter of the event handler callback function. If we log it, as in this code:

const EventEmitter = require('events');
class Emitter extends EventEmitter {}
const eventEmitter = new Emitter();

eventEmitter.on('error', (error) => {
  console.log(error);
});

eventEmitter.emit('error', new Error('Error occurred'));

We will get something like this:

Error: Error occurred
    at evalmachine.<anonymous>:7:28
    at Script.runInContext (vm.js:133:20)
    at Object.runInContext (vm.js:311:6)
    at evaluate (/run_dir/repl.js:133:14)
    at ReadStream.<anonymous> (/run_dir/repl.js:116:5)
    at ReadStream.emit (events.js:198:13)
    at addChunk (_stream_readable.js:288:12)
    at readableAddChunk (_stream_readable.js:269:11)
    at ReadStream.Readable.push (_stream_readable.js:224:10)
    at lazyFs.read (internal/fs/streams.js:181:12)

More Ways to Deal with Events

Node.js will emit one special event without writing any code to emit the event: The newListener . The newListener event is emitted before a listener is added to the internal array of listeners. For example, if we have the following code:

const EventEmitter = require('events');
class Emitter extends EventEmitter {}
const eventEmitter = new Emitter();
eventEmitter.on('newListener', (`event, listener`) => {
console.log(`event`);
});

Then we get something like this logged:

Emitter {
  _events: [Object: null prototype] { newListener: [Function] },
  _eventsCount: 1,
  _maxListeners: undefined }

This happens even when no events are emitted. Whatever is in the handler will be run before the code in event handlers for any other events.

The removeListener function can be used to stop event listener functions from listening to events. This takes two arguments: The first is a string that represents the event name, the second is the function that you want to stop using to listen to events. For example, if we want to stop listening to the “event” event with our listener function, then we can write this:

const EventEmitter = require('events');
class Emitter extends EventEmitter { }
const eventEmitter = new Emitter();

const listener = () => {
  console.log('listening');
}

eventEmitter.on('event', listener)

setInterval(() => {
  eventEmitter.emit('event');
}, 300);

setTimeout(() => {
  console.log("removing");
  eventEmitter.removeListener('event', listener);
}, 2000);

Then we get something like this in the output:

Timeout {
  _called: false,
  _idleTimeout: 2000,
  _idlePrev: [TimersList],
  _idleNext: [TimersList],
  _idleStart: 1341,
  _onTimeout: [Function],
  _timerArgs: undefined,
  _repeat: null,
  _destroyed: false,
  [Symbol(unrefed)]: false,
  [Symbol(asyncId)]: 10,
  [Symbol(triggerId)]: 7 }listening
listening
listening
listening
listening
listening
removing

The event emitter emits the “event” event in the code above once every 300 milliseconds. This is listened to by the listener function, until it’s been prevented from listening again by calling the removeListener function with the “event” as the event name the listener event listener function in the callback of the setTimeout function.

Multiple event listeners can register for a single event. By default, the limit for the maximum number of event listeners is ten. We can change this with the defaultMxListeners function in the EventEmitter class. We can set it to any positive number. If it’s not a positive number, then a TypeError is thrown. If more listeners than the limit are registered then a warning will be output. For example, if we run the following code to register 11 event listeners for the “event” event:

const EventEmitter = require('events');
class Emitter extends EventEmitter { }
const eventEmitter = new Emitter();

const listener = () => {
  console.log('listening');
}

for (i = 1; i <= 11; i++){
  eventEmitter.on('event', listener);
}

eventEmitter.emit('event');

When we run the code above, we get this:

listening
listening
listening
listening
listening
listening
listening
listening
listening
listening
listening
(node:345) MaxListenersExceededWarning: Possible EventEmitter memory leak detected. 11 event listeners added. Use emitter.setMaxListeners() to increase limit

However, if we call setMaxListeners to set it to getMaxListeners() + 1, which is 11 listeners, as seen in the following code:

const EventEmitter = require('events');
class Emitter extends EventEmitter { }
const eventEmitter = new Emitter();
eventEmitter.setMaxListeners(eventEmitter.getMaxListeners() + 1);

const listener = () => {
  console.log('listening');
}

for (i = 1; i <= 11; i++){
  eventEmitter.on('event', listener);
}

eventEmitter.emit('event');

Then we get the following logged:

listening
listening
listening
listening
listening
listening
listening
listening
listening
listening
listening

How to attach and remove event listeners

To get the names of the events that are listened to, we can use the eventNames function. The function takes no arguments and returns an array of event identifiers, which may include strings or symbols.

For example, we can use it as in the following code:

const EventEmitter = require('events');
const eventEmitter = new EventEmitter();
eventEmitter.on('event1', () => {});
eventEmitter.on('event2', () => {});

const sym = Symbol('event3');
eventEmitter.on(sym, () => {});

console.log(eventEmitter.eventNames());

When we run the code above, we get the following logged:

[ 'event1', 'event2', Symbol(event3) ]

To get the maximum number of listeners that can be attached to a single event, we can use the getMaxListeners function. It takes no arguments and returns the current maximum number of listeners that can be attached to one event.

The maximum number of event listeners that can be attached to the event can be set by the setMaxListeners function and the default value is 10. For example, we can use it as in the following example:

const EventEmitter = require('events');
const eventEmitter = new EventEmitter();
console.log(eventEmitter.getMaxListeners());

We can use the listenerCount function to get the number of event listeners attached to an event. It takes one string or symbol argument for the event name and returns an integer with the number of event listeners attached to the event with the event name passed into the argument.

For example, we can use it as in the following code:

const EventEmitter = require('events');
const eventEmitter = new EventEmitter();

for (let i = 1; i <= 5; i++) {
  eventEmitter.on('event', () => { });
}

console.log(eventEmitter.listenerCount('event'));

If the code above is run, we get 5 logged as we attached five event listeners to the event event.

To get the event listeners functions attached to a given event, we can use the listeners function. It takes a string or symbol with the event name and returns an array of event listener functions.

It returns a copy of the listeners rather than the original ones. For instance, we can use it as in the following example:

[ '() => console.log(`Listener ${i} for 'event' event invoked`)',
  '() => console.log(`Listener ${i} for 'event' event invoked`)',
  '() => console.log(`Listener ${i} for 'event' event invoked`)',
  '() => console.log(`Listener ${i} for 'event' event invoked`)',
  '() => console.log(`Listener ${i} for 'event' event invoked`)' ]

To attach an event listener to the beginning of the array listeners for a given event, we can use the prependListener function.

The function takes the event name as the first argument and the event listener function as the second argument. For example, we can write the following code:

const EventEmitter = require('events');
const eventEmitter = new EventEmitter();

for (let i = 1; i <= 5; i++) {
  eventEmitter.on('event', () => console.log(`Listener ${i} for 'event' event invoked`));
}

eventEmitter.prependListener('event', () => console.log('Prepended listener invoked'))

console.log(eventEmitter.listeners('event').map(f => f.toString()));

If we run the code above, we get the following logged:

[ '() => console.log('Prepended listener invoked')',
  '() => console.log(`Listener ${i} for 'event' event invoked`)',
  '() => console.log(`Listener ${i} for 'event' event invoked`)',
  '() => console.log(`Listener ${i} for 'event' event invoked`)',
  '() => console.log(`Listener ${i} for 'event' event invoked`)',
  '() => console.log(`Listener ${i} for 'event' event invoked`)' ]

As we can see, the prepended event listener is in the first slot of the array. If we add eventEmitter.emit('event') to emit the event event, as in the following code:

const EventEmitter = require('events');
const eventEmitter = new EventEmitter();

for (let i = 1; i <= 5; i++) {
  eventEmitter.on('event', () => console.log(`Listener ${i} for 'event' event invoked`));
}

eventEmitter.prependListener('event', () => console.log('Prepended listener invoked'))

console.log(eventEmitter.listeners('event').map(f => f.toString()));

Then, we get the following logged with the console.log statements:

[ '() => console.log('Prepended listener invoked')',
  '() => console.log(`Listener ${i} for 'event' event invoked`)',
  '() => console.log(`Listener ${i} for 'event' event invoked`)',
  '() => console.log(`Listener ${i} for 'event' event invoked`)',
  '() => console.log(`Listener ${i} for 'event' event invoked`)',
  '() => console.log(`Listener ${i} for 'event' event invoked`)' ]
Prepended listener invoked
Listener 1 for 'event' event invoked
Listener 2 for 'event' event invoked
Listener 3 for 'event' event invoked
Listener 4 for 'event' event invoked
Listener 5 for 'event' event invoked

We also see that the listener that we prepended to the event listener array is invoked first since the listeners are handled in the same order as they’re added into the array.

If we want to prepend an event listener that only handles the emitted event once, we can use the prependOnceListener instead. For example, we can use it as in the following code:

const EventEmitter = require('events');
const eventEmitter = new EventEmitter();
for (let i = 1; i <= 5; i++) {
  eventEmitter.on('event', () => console.log(`Listener ${i} for 'event' event invoked`));
}
eventEmitter.prependOnceListener('event', () => console.log('Prepended once listener invoked'))

eventEmitter.emit('event');
eventEmitter.emit('event');

If we run the code above, we can see the following logged in the console.log statements:

Prepended once listener invoked
Listener 1 for 'event' event invoked
Listener 2 for 'event' event invoked
Listener 3 for 'event' event invoked
Listener 4 for 'event' event invoked
Listener 5 for 'event' event invoked
Listener 1 for 'event' event invoked
Listener 2 for 'event' event invoked
Listener 3 for 'event' event invoked
Listener 4 for 'event' event invoked
Listener 5 for 'event' event invoked

As we can see, the listener that we prepended with the prependOnceListener only runs once. This is what we expect if we attach an event listener with the prependOnceListener function.

If we want to remove all event listeners for a given event, we can use the removeAllListeners function. It takes a string or symbol argument for the event identifier.

Note that it’s bad practice to remove event listeners that are attached elsewhere in the code when the EventEmitter is created in some other component of your program. It returns a reference to the EventEmitter so that the calls can be chained.

For instance, we can write the following code to remove all event listeners for the event event:

const EventEmitter = require('events');
const eventEmitter = new EventEmitter();
for (let i = 1; i <= 5; i++) {
  eventEmitter.on('event', () => console.log(`Listener ${i} for 'event' event invoked`));
}
eventEmitter.prependOnceListener('event', () => console.log('Prepended once listener invoked'))

console.log('Before remove all listenersn', eventEmitter.listeners('event').map(f => f.toString()));

eventEmitter.removeAllListeners('event');

console.log('After remove all listenersn', eventEmitter.listeners('event'));

When the code above is run, we get the following logged:

Before remove all listeners
 [ '() => console.log('Prepended once listener invoked')',
  '() => console.log(`Listener ${i} for 'event' event invoked`)',
  '() => console.log(`Listener ${i} for 'event' event invoked`)',
  '() => console.log(`Listener ${i} for 'event' event invoked`)',
  '() => console.log(`Listener ${i} for 'event' event invoked`)',
  '() => console.log(`Listener ${i} for 'event' event invoked`)' ]
After remove all listeners
 []

To remove a single event listener for a given event, we can use the removeListener function. It takes an argument with the string or symbol for an event identifier, and a reference to the listener that’s attached to the given event.

It returns a reference to the EventEmitter so that the calls can be chained. We can use it as in the following code:

const EventEmitter = require('events');
const eventEmitter = new EventEmitter();

const listener1 = () => console.log('listener1 invoked');
const listener2 = () => console.log('listener2 invoked');

eventEmitter.on('event', listener1);
eventEmitter.on('event', listener2);

console.log('Before remove listenersn', eventEmitter.listeners('event').length);

eventEmitter
  .removeListener('event', listener1)
  .removeListener('event', listener2);

console.log('After remove listenersn', eventEmitter.listeners('event').length);

When we run the code above, we get the following logged with the console.log statements:

Before remove listeners
 2
After remove listeners
 0

As we can see, the removeListener can be chained and lets us remove the listeners one at a time. The listeners that were removed are no longer listening to the events.

If a single listener is attached to an event multiple times, then it might be called the number of times that the event listener is attached to remove all the event listeners.

If the removeListener function is called once in this scenario, it will remove the most recently added event listener. The event listeners array for the given event will be updated when the event listener is removed.

To get an array of event listeners for an event including the wrappers, we can use the rawListeners function. For example, we can use it as the following code:

const EventEmitter = require('events');
const eventEmitter = new EventEmitter();

const listener1 = () => console.log('listener1 invoked');
const listener2 = () => console.log('listener2 invoked');

eventEmitter.once('event', listener1);
eventEmitter.on('event', listener2);

const rawListeners = eventEmitter.rawListeners('event');
rawListeners[0].listener();

If we run the code above, we see that listener1 invoked is logged. This is because that the eventEmitter.once wraps the listener function that’s passed into it.

This is returned along with the actual event listener. Therefore, we can run the actual event listener function by running rawListeners[0].listener();.

This doesn’t work with the second listener because there’s no wrapper to wrap the listener function when we call eventEmitter.on to attach a listener.

An important feature of Node.js is asynchronous programming. This means that code in Node.js may not be executed sequentially. Therefore, data may not be determined in a fixed amount of time.

This means that to get all the data we need, we have to pass data around the app when the data is obtained. We can emit events and handle them within a Node.js app.

When an event with a given name is emitted, the event can listen to the listener if the listener is specified to listen to the event with the name. Event emitter functions are called synchronously.

The event listener code is a callback function that takes a parameter for the data and handles it. Node.js has an EventEmitter class that can be extended by a new class that we create to emit events that can be listened to by event listeners.

With the EventEmitter class, we can add and remove event listeners with built-in functions. We can remove all of them at the same time or one at a time.

However, we shouldn’t remove listeners from event emitters that aren’t written by us as it causes confusion to other people that are working on the code since other programmers do not expect built-in listeners from other modules to be removed.

Categories
JavaScript

Handy Tips for Working with Numbers in JavaScript

JavaScript, like any other programming language, has many handy tricks that let us write our programs more easily. In this article, we will look at how to do different things that involve numbers, like generating a number array, generating random numbers, and shuffling numbers.

Generating a Number Array within a Range

To generate a number from a minimum to a maximum number, we can do it in a few ways. If we want each entry to increment by 1, we can use the Array.from method. The Array.from method takes an object which has the length property.

To generate a number within a range, like from 1 to 10, we can make a constant for the maximum number and another one for the minimum number. Then the length would be the maximum minus the minimum plus one.

We need to add one since the maximum minus the minimum is one less than the length we want. The second argument of the Array.from method is a function that lets us map the values to the ones we want.

The first parameter is the value of the original array since we didn’t pass in an array into the first argument, this parameter isn’t useful for us.

The second argument is the index of the array, which will range from 0 to the length which we specified as the length property minus 1. So if we want to generate an array of numbers from 1 to 10, we can write:

const max = 10;
const min = 1;
const arr = Array.from({
  length: max - min + 1
}, (v, i) => min + i);
console.log(arr)

In the code above, we specified the length property of the array that we’ll generate, which is max — min + 1 or 10 – 1+1, which is 10. That’s the length we want. In the second argument, we have a function that maps the index i into min + i, which is will be 1 + 0 , 1 + 1 , 1 + 2 , …, up to 1 + 9 . Then if we run the console.log statement in the last line of the code above, we get:

[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]

It’s easy to generate different kinds of number arrays by adjusting the function in the second parameter since it maps to any number we want. To generate the first n odd numbers, we can write:

const arr = Array.from({
  length: 10
}, (v, i) => 2 * i + 1);
console.log(arr)

In the code above, we have a function that generates odd numbers from a formula. 2 * i + 1 always generates an odd number since 2 times any integer is an event number, so if we add 1 to it, then it will become an odd number. It’s useful for any number sequence that has a pattern that can be put into a formula or be expressed in terms of conditionals or other flow control statements.

Generate Random Numbers

The JavaScript standard library has a Math object, which has a random method to generate random numbers between 0 and 1. It takes no arguments.

By itself, it’s of limited use since it can only generate numbers between 0 and 1, but we can easily use it to generate random numbers in any range.

For example, if we want to generate a number between a minimum and a maximum number, we can write the following code:

const min = 1;
const max = 100;
const num = Math.random() * (max - min) + min;
console.log(num);

In the code above, we have a formula that has the min number as the minimum number, then we add Math.random() * (max — min) to min. max-min would be positive since max is bigger than min and Math.random() returns a number between 0 and 1 so it would either be 0 or positive.

If max is the same as min, then we get min as the result of num, which is the lowest number we’ll accept, and if Math.random() is 0 we get the same thing.

If Math.random() is 1, we get max — min + min which is the same as max. This means that the formula would never be outside of the number range between min and max, which is what we want. The formula above will get us any floating-point number. If want integer results only, we can write:

let min = 1;
let max = 10;
min = Math.ceil(min);
max = Math.floor(max);
const num = Math.floor(Math.random() * (max - min)) + min;
console.log(num);

In the code above, we rounded down our result to the nearest integer with the Math.floor(Math.random() * (max — min)) method call. This makes sure the result generated would be between 1 inclusive and 10 exclusive.

Generate an Array of Random Numbers

We can easily expand the code above to generate an array of random numbers. Using the Array.from method that we used above, we can create the array like in the following code:

const arr = Array.from({
  length: 10
}, (v, i) => {
  const min = 1;
  const max = 10;
  return Math.random() * (max - min) + min
});
console.log(arr)

In the second argument of the Array.from method call, we modified the function for mapping the values to the number generator function with the code inside it as the same code that we used to generate random numbers.

Again, we can specify the length property of the object in the first argument to the length of the array that we want. If we run the code above, the console.log output from the statement on the last line should log something like:

[2.09822597784169, 9.485219511793478, 4.905513590655689, 5.428402066955793, 5.874266428138649, 6.288415362686418, 2.768483595321933, 9.849179787675595, 1.1291148797430082, 6.725810669834928]

Also, we can use the integer generator code that we have before in the same manner, like in the code below:

const arr = Array.from({
  length: 10
}, (v, i) => {
  let min = 1;
  let max = 10;
  min = Math.ceil(min);
  max = Math.floor(max);
  return num = Math.floor(Math.random() * (max - min)) + min;
});
console.log(arr)

We have the same code that we used to generate integers as before, except that we used it to generate an array of random integers. If we run the code above, we should get something like the following:

[8, 3, 5, 4, 4, 9, 9, 1, 1, 1]

from the console.log statement above.

Shuffling an Array of Numbers

With the sort method built into the JavaScript array objects, we can use it easily to sort arrays. The sort method takes a function that lets us compare 2 entries in the array to let us set the sort order between 2 elements based on the conditions that we specify.

The function that we pass into the sort method takes 2 parameters, the first is an element which we call a, and the second is an element which we call b, and we compare a and b to determine which should be sorted above the other in the array. The sort function expects a number to be returned by the callback.

If the value that’s returned by our function is negative, then a will be in an array index that’s lower than b. That is, a comes before b. If the number is positive, then b will come before a. If the function returns 0, then a and b will stay in their original position.

If we want to shuffle the array, we can just return a random number from it — the value of a or b will not have an impact on the sort. We can once again use the Math.random() method to help us generate a random return value for the function we pass into the sort method. We can write the following code to sort each entry in random order (shuffle the array):

let nums = [49, -151, 88, -133, -164, 78, 117, -25, 76, -40];
nums = nums.sort(() => {
  return Math.random() - 0.5
});
console.log(nums);

In the code above, our sort method has a function which generates a random number between -0.5 and 0.5, which means there’s a chance that each element either stays in place or gets shuffled, according to what the sort method will do depending on the return value.

The Math.random() only returns numbers between 0 and 1. However, we can easily make it more useful by extending it in the ways that we did above. We can specify a range between a minimum and a maximum number and Math.random() * (max — min) + min to generate a number between a range. Also, we can pass it into the callback function of the sort method by returning Math.random() — 0.5 to generate a number between -0.5 and 0.5, which means that it can be shuffled (or not) depending on the sign of the generated number and whether it’s 0 or not.

Categories
TypeScript

Cool New Features Released in TypeScript 3.7

With the release of TypeScript 3.7, some great new features that are included from ES2020 that are now part of TypeScript. The new features include things like optional chaining, nullish coalescing, check for uncalled functions, and more.

In this article, we’ll look at some of them in more detail.

Optional Chaining

Optional chaining is a feature that lets us get a deeply nested property of an object without worrying if any of the properties are null or undefined.

We can use the ?. operator to get the properties of an object instead of using the usual dot operator. For example, instead of writing:

let x = a.b.c.d;

We write:

let x = a?.b?.c?.d;

With the optional chaining operator, we don’t have to worry about properties b or c being null or undefined, which saves us from writing lots of code to check is these properties exist.

If any of the intermediate properties are null or undefined, then undefined is returned instead of crashing the app with errors.

This means that we no longer have to write something like:

let x = a && a.b && a.b.c && a.b.c.d;

To get the d property from the a object.

One thing to be careful is that if strictNullChecks flag is on, then we’ll get errors if we operate on an expression with the optional chaining operator inside a function with a parameter that has optional parameter as the operand.

For example, if we write:

let a = { b: { c: { d: 100 } } };
const divide = (a?: { b: { c: { d: 100 } } }) => {
  return a?.b?.c?.d / 100;
}

Then we get the error ‘Object is possibly ‘undefined’.(2532)’.

Nullish Coalescing

The nullish coalescing operator lets us assign a default value to something when something is null or undefined.

An expression with the nullish coalescing looks like:

let x = foo ?? bar;

Where the ?? is the nullish coalescing operator.

For example, we can use it as follows:

let x = null;
let y = x ?? 0;
console.log(y); // 0

Then we get 0 as the value of y .

This is handy because the alternative was to use the || operator for assigning default values. Any falsy value on the left operand would cause the default value on the right operand to be assigned, which we might not always want.

The nullish coalescing operator only assigns the default value when the left operand is null or undefined.

For example:

let x = 0;
let y = x || 0.5;

In the code above, 0 is a valid value that can be assigned to y, but we still assign the default value of 0.5 to it because 0 is falsy, which we don’t want.

Assertion Functions

TypeScript 3.7 comes with the asserts keyword which let us write our own functions to run some checks on our data and throws an error if the check fails.

For example, we can write the following to check if a parameter passed into our assertion function is a number:

function assertIsNumber(x: any): asserts x is number {
    if (typeof x === 'number') {
        throw new Error('x is not a number');
    }
}
assertIsNumber('1');
assertIsNumber(1);

When we run the code above, we should get ‘Uncaught Error: x is not a number’.

In the code above, the asserts keyword checks whatever condition comes after it.

It’s a function that returns void which means that it doesn’t return anything. It can only throw errors if it doesn’t meet the condition we define.

Better Support for Functions that Return the Never Type

With TypeScript 3.7, now the TypeScript compiler recognizes functions that returns the never type is run in a function that returns some other type.

For example, before TypeScript 3.7, we have to write the following to avoid an error:

const neverFn = (): never => {
    throw new Error();
};

const foo = (x: string | number): number => {
    if (typeof x === 'string') {
        return +x;
    }
    else if (typeof x === 'number') {
        return x;
    }
    return neverFn();
}

The code above would get us the error “Function lacks ending return statement and return type does not include ‘undefined’.” Because we did add return before the neverFn() function call.

TypeScript versions earlier than 3.7 don’t recognize the never function as a valid path because it doesn’t allow a path in the code that returns undefined if a return type is specified.

Now removing the return in return neverFn(); will work if TypeScript 3.7 is used to compile the code.

Allowing Some Recursive Type Aliases

Type aliases that aren’t assigned to itself are now allowed with TypeScript 3.7. For example, the following is still not allowed:

type Bar = Bar;

since it just replaces the Bar type with itself forever.

If we try to compile the code above, we would get the error “Type alias ‘Bar’ circularly references itself. (2456)“.

However, now we can write something like:

interface Foo { };
interface Baz { };
type Bar = Foo | Baz | Bar[];

This is because the Bar[] type isn’t directly replacing Bar , so this type of recursion is allowed.

Generating Declaration Files when AllowJs Flag is On

Before TypeScript 3.7, we can’t generate declaration files when the --allowJs flag is on, so TypeScript code that is compiled with JavaScript can’t have any declaration files generated.

This means that type checking can’t be done with JavaScript files that are being compiled, even if they have JSDoc declarations.

With this change, now type checking can be done with those JavaScript files.

Now we can write libraries with JSDoc annotated JavaScript and support TypeScript users with the same code.

The TypeScript compiler since 3.7 will infer the types of the JavaScript code from the JSDoc comments.

Uncalled Function Checks

Forgetting to call a function by omitting the parentheses is a problem that sometimes causes bugs. For example, if we write the following:

const foo = () => { };

const bar = () => {
    if (foo) {
        return true;
    }
}

We’ll get the error “This condition will always return true since the function is always defined. Did you mean to call it instead?(2774)” when we write the code above and try to compile it with the TypeScript 3.7 compiler.

This code wouldn’t give any errors before version 3.7.

Conclusion

As we can see, TypeScript 3.7 gives us a lot of useful new features that aren’t available before. Optional chaining and nullish coaslecing operators are handy for avoiding null or undefined errors.

Recognizing function calls that return the never type is also handy for writing code with paths that don’t always return.

Having recursive type aliases help with combining some kinds of types into one alias.

For developers that write libraries, TypeScript 3.7 can infer types from JavaScript files that are compiled with the TypeScript compiler by checking the JSDoc comments.

Finally, uncalled functions are now checked by the TypeScript compiler if they’re written like we’re trying to access it as a property.