Async Functions

Asynchronous functionality is what differ JavaScript from other languages, and ES8 made it easier to deal with this nature. Previously, we dealt with the async functionality with callbacks and nested callbacks which caused a problem known as the callback hell. Then, promises were born. They were a lot better and solved the problems that the callbacks introduced but still had this type of unnatural way of dealing with async code. Just to have async/await which let the developer deal with async code naturally and with no gimmicks.

When an async function is called, it returns a Promise. When the async function returns a value, the Promise will be resolved with the returned value. When the async function throws an exception or some value, the Promise will be rejected with the thrown value.

An async function can contain an await expression, which pauses the execution of the async function and waits for the passed Promise's resolution, and then resumes the async function's execution and returns the resolved value. Let's take an example.

function resolveAfter2Seconds(x) {
    return new Promise(resolve => {
        setTimeout(() => {
            resolve(x);
        }, 2000);
    });
}


async function add1(x) {
    const a = await resolveAfter2Seconds(20);
    const b = await resolveAfter2Seconds(30);
    return x + a + b;
}

add1(10).then(v => {
    console.log(v); // prints 60 after 4 seconds.
});


async function add2(x) {
    const p_a = resolveAfter2Seconds(20);
    const p_b = resolveAfter2Seconds(30);
    return x + await p_a + await p_b;
}

add2(10).then(v => {
    console.log(v); // prints 60 after 2 seconds.
});

The good part about the async functions is that they can be implemented naturally into the language with try..catch statement when trying to handle errors unlike the Promise where they require that we chain catch to them. Consider the following bit.

function getProcessedData(url) {
    return downloadData(url) // returns a promise
        .catch(e => {
            return downloadFallbackData(url) // returns a promise
                .then(v => {
                    return processDataInWorker(v); // returns a promise
                });
        })
        .then(v => {
            return processDataInWorker(v); // returns a promise
        });
}

The previous bit could be written with async function like the following.

async function getProcessedData(url) {
    let v;
    try {
        v = await downloadData(url);
    } catch (e) {
        v = await downloadFallbackData(url);
    }
    return processDataInWorker(v);
}

Shared Memory and Atomics

SharedArrayBuffer and Atomics were introduced to improve the concurrency in JavaScript. Shared Array Buffer is a primitive building block for higher-level concurrency abstractions. It allows us to share the bytes of a SharedArrayBuffer object between multiple service workers and the main thread (the buffer is shared, to access the bytes, wrap it in a Typed Array).
This kind of sharing has two benefits:

  • You can share data between workers more quickly.
  • Coordination between workers becomes more straightforward and faster (compared to postMessage()).

Atomics provides static methods to for atomic operations used for synchronization, waiting to be notified (instead of using while loop for instance), and for atomic operations.

For more information about the subject, you can refer to Lars T. Hansen's tutorial about the topic (this feature was his proposal). Another reference is 2ality's.

Object.values() and Object.entries()

The Object.values() is a complementary method to the Object.keys(). While the Object.keys() method returns an array of a given object's own enumerable properties, in the same order as that provided by a for...in loop, Object.values() returns an array of the values of those properties. Let's see that method in some examples.

var obj = { foo: 'bar', baz: 42 };
console.log(Object.values(obj)); // ['bar', 42]

// array like object
var obj = { 0: 'a', 1: 'b', 2: 'c' };
console.log(Object.values(obj)); // ['a', 'b', 'c']

// array like object with random key ordering
var an_obj = { 100: 'a', 2: 'b', 7: 'c' };
console.log(Object.values(an_obj)); // ['b', 'c', 'a']

// getFoo is property which isn't enumerable
var my_obj = Object.create({}, { getFoo: { value: function() { return this.foo; } } });
my_obj.foo = 'bar';
console.log(Object.values(my_obj)); // ['bar']

// non-object argument will be coerced to an object
console.log(Object.values('foo')); // ['f', 'o', 'o']

The Object.entries() method returns an array of a given object's own enumerable property [key, value] pairs, in the same order as that provided by a for...in loop.

const obj = {
    foo: 'bar',
    baz: 42
};
console.log(Object.entries(obj)); // [ ['foo', 'bar'], ['baz', 42] ]

// array like object
const obj = {
    0: 'a',
    1: 'b',
    2: 'c'
};
console.log(Object.entries(obj)); // [ ['0', 'a'], ['1', 'b'], ['2', 'c'] ]

// array-like object with random key ordering
const anObj = {
    100: 'a',
    2: 'b',
    7: 'c'
};
console.log(Object.entries(anObj)); // [ ['2', 'b'], ['7', 'c'], ['100', 'a'] ]

// getFoo is property which isn't enumerable
const myObj = Object.create({}, {
    getFoo: {
        value() {
            return this.foo;
        }
    }
});
myObj.foo = 'bar';
console.log(Object.entries(myObj)); // [ ['foo', 'bar'] ]

// non-object argument will be coerced to an object
console.log(Object.entries('foo')); // [ ['0', 'f'], ['1', 'o'], ['2', 'o'] ]

// returns an empty array for any primitive type, since primitives have no own properties
console.log(Object.entries(100)); // [ ]

// iterate through key-value gracefully
const obj = {
    a: 5,
    b: 7,
    c: 9
};
for (const [key, value] of Object.entries(obj)) {
    console.log(`${key} ${value}`); // "a 5", "b 7", "c 9"
}

// Or, using array extras
Object.entries(obj).forEach(([key, value]) => {
    console.log(`${key} ${value}`); // "a 5", "b 7", "c 9"
});

String.prototype.padStart() and String.prototype.padEnd()

The String.prototype.padStart() method pads the current string with another string (repeated, if needed) so that the resulting string reaches the given length. The padding is applied from the start (left) of the current string. And String.prototype.padEnd() is pretty much the same but having the padding applied from the end (right) of the current string.

Both have the following syntax.

str.padStart(targetLength [, padString]);
str.padEnd(targetLength [, padString]);

Where targetLength is the length of the resulting string once the current string has been padded. If the value is lower than the current string's length, the current string will be returned as is. And padString (optional value) is the string to pad the current string with. If this string is too long to stay within the target length, it will be truncated, and the left-most part will be applied.

'abc'.padStart(10);         // "       abc"
'abc'.padStart(10, "foo");  // "foofoofabc"
'abc'.padStart(6,"123465"); // "123abc"
'abc'.padStart(8, "0");     // "00000abc"
'abc'.padStart(1);          // "abc"

'abc'.padEnd(10);          // "abc       "
'abc'.padEnd(10, "foo");   // "abcfoofoof"
'abc'.padEnd(6, "123456"); // "abc123"
'abc'.padEnd(1);           // "abc"

Object.getOwnPropertyDescriptors()

This method permits examination of the precise description of all own properties of an object. A property in JavaScript consists of a string-valued name and a property descriptor. Further information about property descriptor types and their attributes can be found in Object.defineProperty().

const obj = {foo: 'bar'};

Object.getOwnPropertyDescriptors(obj);
// foo: {value: "bar", writable: true, enumerable: true, configurable: true}

Trailing Commas in Function Parameter Lists and Calls

Trailing commas (sometimes called "final commas") can be useful when adding new elements, parameters, or properties to JavaScript code. If you want to add a new property, you can just add a new line without modifying the previously last line if that line already uses a trailing comma. This makes version-control diffs cleaner, and editing code might be less troublesome.

This is similar to the trailing commas in Ruby's and PHP's arrays and Python's lists and dictionaries.

var arr = [
  1, 
  2, 
  3, 
];

arr; // [1, 2, 3]
arr.length; // 3

var object = { 
  foo: "bar", 
  baz: "qwerty",
  age: 42,
};

function f(p) {}
function f(p,) {} 

(p) => {};
(p,) => {};