Categories
Modern JavaScript

Best Features of ES2018 — New Regex Features

Since 2015, JavaScript has improved immensely.

It’s much more pleasant to use it now than ever.

In this article, we’ll look at the best features of ES2018.

Regex Property Escapes

Unicode has properties, which are metadata describing it.

There are properties like Lowercase_Letter to describe lowercase letters, White_space to describe white spaces etc.

There’re several types of properties.

Enumerated property is a property value whose values are few and named.

General_Category is an enumerated property.

Close enumerated property ios an enumerated property whose set values is fixed.

Boolean property is a close enumerated property whose value is true or false .

Numeric properties have values that are real numbers.

String-valued property is a property whose values are strings.

Catalog property is an enumerated property that may be extended as Unicode changes.

Miscellaneous property is a property whose values aren’t any of the above.

There are various kinds of matching properties and property values.

The properties are loose matching, so General_Category is considered the same as GeneralCategory and other variants.

Unicode Property Escapes for Regex

We can use the \p characters to escape the Unicode properties.

This must be used with the /u flag to enable Unicode mode.

\p is the same as p without Unicode mode.

For instance, we can write:

const result = /^\p{White_Space}+$/u.test(' ')

and result would be true .

This means \p{White_Space} matches whitespace.

This is more descriptive than regular regex patterns.

We can also write:

const result = /^\p{Letter}+$/u.test('abc')

to match letters.

To match Greek letters, we write:

const result = /^\p{Script=Greek}+$/u.test('μ')

And we can match Latin letters with:

const result = /^\p{Script=Latin}+$/u.test('ç')

Long surrogate characters can also be matched:

const result = /^\p{Surrogate}+$/u.test('\\u{D83D}')

Lookbehind Assertions

A lookbehind assertion is a construct in a regex that specifies what the surroundings of the current location must look like.

For instance, we can write:

const RE_DOLLAR_PREFIX = /(?<=\$)\d+/g;

const result = '$123'.replace(RE_DOLLAR_PREFIX, '456');

We have the (?<=\$) group to look for digits with a dollar sign before it.

Then when we call replace to replace the number, we just replace the number.

We searched for something with $ and digits after it with that regex.

So result is '$456' .

This doesn’t work if the prefix should be part of the previous match.

We can also add a ! to add a negative lookbehind assertion.,

For instance, we can write:

const RE_DOLLAR_PREFIX = /(?<!\$)baz/g;

const result = '&baz'.replace(RE_DOLLAR_PREFIX, 'qux');

The regex looks for baz without a dollar sign before it.

So if we called replace as we did, we get '&qux’ returned since $ isn’t in the string.

s (dotAll) Flag for Regex

The dotAll dlag is a enahnance of the . flag in a regex.

The . in a regex doesn’t match line terminator characters.

So if we have:

/^.$/.test('\n')

We get false .

To match line terminator characters, we’ve to wite:

/^[^]$/.test('\\n')

to match everything except no character or:

/^\[\s\S]$/.test('\n')

to match space or non-whitespace.

There’re various kinds of line termination characters.

They include:

  • U+000A line feed (LF) (\n)
  • U+000D carriage return (CR) (\r)
  • U+2028 line separator
  • U+2029 paragraph separator

With ES2018, we can use the /s flag to match something that ends with a line terminator:

const result = /^.$/s.test('\\n')

So result should be true .

The long name is dotAll .

So we can write:

/./s.dotAll

and that returns true .

Conclusion

Regex property escapes, lookbehind assertions, and the dotAll flag new additions to JavaScript regexes that let us match various special cases.

Categories
Modern JavaScript

Best Features of ES2018- Async Iteration

Since 2015, JavaScript has improved immensely.

It’s much more pleasant to use it now than ever.

In this article, we’ll look at the best features of ES2018.

Async Iteration

With ES6, JavaScript introduced the for-of loop.

It lets us synchronously iterate through iterable objects.

However, there’s no way to iterate asynchronously.

ES2018 introduced the async iteration feature to fill this gap.

Synchronous iteration works with any object that has the Symbol.iterator method, which is a generator method.

It returns each item with sequentially when the next next method is called.

The returned object has the value returned from the generator, which is the current entry that the iterator hits.

It also has the done property to indicate whether all th values have been returned.

For instance, if we have:

const iterable = ['foo', 'bar'];
const iterator = iterable[Symbol.iterator]();

Then when we call:

iterator.next()

the first time, we get:

{ value: 'foo', done: false }

Then when we call it again, we get;

{ value: 'b', done: false }

And when we call it one more time, we get:

{ value: undefined, done: true }

The iteration is synchronous, so we can’t iterate asynchronously.

We want to be able to call promises in an iterable object one by one.

This is solved with the for-await-of loop.

For instance, we can use it by writing:

async function main() {
  const arr = [
    Promise.resolve('foo'),
    Promise.resolve('bar'),
  ];
  for await (const x of arr) {
    console.log(x);
  }
}
main();

We have an array of promises that we iterate through.

They’ll be run one by one.

So we get:

foo
bar

logged in the console log.

The for-await-of loop also works with synchronous iterables.

So we can write:

async function main() {
  for await (const x of ['foo', 'bar']) {
    console.log(x);
  }
}
main();

And we get the same results logged.

Async Generators

Async generators let us create async iterable.

An async generator is just a generator function that returns promises.

For instance, we can create an async generator function by writing:

async function* createAsyncIterable(arr) {
  for (const elem of arr) {
    yield elem;
  }
}

A normal generator returns a generator object.

And each invocation of the next method returns an object with the properties value and done .

And an async generator returns a generator object.

Each next call returns a promise for an object with properties value and done .

When we call an async generator, the JavaScript engine will wait for the promise to be settled before calling the next one.

Every async generator has a queue with promises to be settled with yield or throw .

And when next is called, a promise is queued.

And unless the async generator is already running, it resumes and waits for the promise to be finished.

It can finish with yield , throw or return await .

Once it’s finished the promise is returned.

The result of a settled promise is delivered in an async manner.

Conclusion

The for-await-of loop lets us iterate through async generators by returning promises of each value sequentially.

Categories
Modern JavaScript

Best Features of ES2018 — Async Generators

Since 2015, JavaScript has improved immensely.

It’s much more pleasant to use it now than ever.

In this article, we’ll look at the best features of ES2018.

yield* in Async Generators

yield* in async generator works like how they work with normal generators.

If we have:

async function* gen() {
  yield 'foo';
  yield 'bar';
  return 'baz';
}

Then we can use it by writing:

(async function() {
  for await (const x of gen()) {
    console.log(x);
  }
})();

And we get:

foo
bar

logged.

Like with normal generators, the returned value is ignored by the for-of loop.

The operand of yield* can be any async iterable.

Sync iterables are automatically converted to async ones like with for-await-of.

Async Iteration Errors

In normal generators, next can throw exceptions.

With async generators, next can reject promises it returns.

For instance, we can write:

async function* asyncGen() {
  throw new Error('error');
}

to throw an error in an async generator.

Then we can catch the error by writing:

asyncGen().next()
  .catch(err => console.log(err));

We get the error object logged in the catch callback.

next return the rejected promise.

Different Between Async Function and Async Generator Function

An async function returns immediately with a promise.

The promise is fulfilled with return or rejected with throw .

So we can either write:

(async function () {
    return 'foo';
})()
.then(x => console.log(x));

or:

(async function() {
  throw new Error('error');
})()
.catch(x => console.log(x));

Async generators return immediately with an async iterable.

And each call of next returns a promise with yield x to fulfill the current promise with {value: x, done: false} .

It can also throw an error with the current promise with err .

So we can write:

async function* genFn() {
  yield 'foo';
}
const gen = genFn();
gen.next().then(x => console.log(x));

Then x is {value: “foo”, done: false} .

Turning an Async Iterable into an Array

We can turn an async variable into an array by looping through the async iterable and then pushing a value into a regular array.

For instance, we can write:

async function convertToArr(asyncIterable) {
  const result = [];
  const iterator = asyncIterable[Symbol.asyncIterator]();
  for await (const v of iterator) {
    result.push(v);
  }
  return result;
}

to create a function that takes an asyncIterable and then push the items into an array and return it.

It returns a promise that resolved to the result array.

Then we can use it by writing:

async function* genFn() {
  yield 'foo';
  yield 'bar';
}

(async () => {
  const arr = await convertToArr(genFn());
  console.log(arr);
})()

We created an async generator and passes the returned result of it to our convertToArr function that we created earlier.

Since it returns a promise that resolves to an array, we’ll see the value when we log it.

Internal Properties of Async Generators

Async generators have 2 internal properties.

[[AsyncGeneratorState]] has the state the generator is currently in.

It can be "suspendedStart", "suspendedYield", "executing", "completed" or undefined .

It’s undefined if it’s not fully initialized.

[[AsyncGeneratorQueue]] holds the pending invocation of next , throw or return .

Each queue has 2 fields, which are [[Completion]] and [[Capability]] .

[[Completion]] has the parameter for next , throw , or return that leads to the entry to be enqueued.

next , throw , or return indicates the method that created the entry and determines what happens after dequeuing.

[[Capability]] is the capability of the promise.

Conclusion

yield *can be used with async generators to call another generator.

There are also big differences between async functions and async generators.

Categories
Modern JavaScript

Best Features of ES2017 — String and Object Methods

Since 2015, JavaScript has improved immensely.

It’s much more pleasant to use it now than ever.

In this article, we’ll look at the best features of ES2017.

Object.entries()

Object.entries() is an object static method introduced with ES2017.

It returns an array of key-value pairs of an object with each key-value pair in an array.

For instance, if we have:

const obj = {
  foo: 1,
  bar: 2
};

for (const [key, value] of Object.entries(obj)) {
  console.log(key, value);
}

Object.entries takes an object and returns the key-value pairs.

Then we loop through the key-value pairs by destructuring the key-value pair array.

Maps via Object.entries()

Since Object.entries returns an array of key-value pairs in an array and the Map constructor takes that array, we can convert an object into a map with it.

For instance, we can write:

const map = new Map(Object.entries({
  foo: 1,
  bar: 2,
}));
console.log(JSON.stringify([...map]));

Then we get:

[["foo",1],["bar",2]]

Object.values()

Object.values() returns an array of values from the object.

It also takes an object as its argument.

For instance, we can write:

const obj = {
  foo: 1,
  bar: 2
};

Object.values(obj)

Then we get [1, 2] returned.

Object.getOwnPropertyDescriptors()

The Object.getOwnPropertyDescriptors method lets us get the property descriptors of an object.

It only returns the descriptor of non-inherited properties.

For instance, we can write:

const obj = {
  [Symbol('foo')]: 100,
  get bar() {
    return 'abc'
  },
};
console.log(Object.getOwnPropertyDescriptors(obj));

Then we get:

{
  bar: {
    configurable: true
    enumerable: true
  },
  Symbol(foo): {
    configurable: true
    enumerable: true
    value: 100
    writable: true
  }
}

configurable indicates whether we can change the property descriptors.

enumerable indicates whether the property is listed with the for-in loop.

value is the property value.

And writable indicates whether the property value can be changed.

We can use it to copy properties into an object.

If we just copy the value, then we miss all the other property descriptors.

Instead, we can call Object.getOwnPropertyDescriptors to get the object.

So we can write:

const source = {
  foo: 1
};
const target = {};

Object.defineProperties(target, Object.getOwnPropertyDescriptors(source));
console.log(Object.getOwnPropertyDescriptor(target, 'foo'));

Then we get all the property descriptors of source copied to target .

And we can set the property descriptors of target.foo to check if everything has been copied over.

String Methods

ES2017 introduced new string method.

New methods include the padStart and padEnd methods.

padStart lets us pad a string to the given name by adding one or more characters repeated to the start of the string.

For instance, if we have:

'x'.padStart(5, 'yz')

then we get:

"yzyzx"

Likewise, there’s a padEnd method that pads a string to a given length with one or more characters repeated to the end of the string.

For instance, we can write:

'x'.padEnd(5, 'yz')

Then we get:

"xyzyz"

returned.

In each method, the first argument is the length, and the 2nd is the string that we want to pad our original string with.

Conclusion

ES2017 has some handy object and string methods we can use.

Categories
Modern JavaScript

Best Features of ES2017 —Async Functions Pitfalls

Since 2015, JavaScript has improved immensely.

It’s much more pleasant to use it now than ever.

In this article, we’ll look at the best features of ES2017.

Properties of Async Functions

Async functions don’t wrap promises that we return.

For instance, if we have:

async function asyncFunc() {
  return Promise.resolve('foo');
}

Then if we have:

asyncFunc()
  .then(x => console.log(x))

Then x is 'foo' .

If we return rejected promise, then the thrown error value will be in the catch callback.

For instance, we can write:

async function asyncFunc() {
  return Promise.reject(new Error('error'));
}

Then if we write:

asyncFunc()
  .catch(err => console.error(err));

and error is the Error instance.

Async Function Tips

We should never forget await if our code on the right side of the = is a promise.

For example, we shouldn’t write:

async function asyncFunc() {
  const value = promiseFunc();
  //...
}

That won’t work since there’s no await to wait for the result of the promise.

await makes sense even if the function doesn’t return anything.

We can use the promise a signal to pause the function until something is done.

For instance, we can write:

async function asyncFunc() {
  await sleep(2000);
  //...
}

to make the function pause for 2 seconds with our own sleep function.

We can implement sleep however we like to as long as it returns a promise.

We Don’t Always Need await

If we just want to invoke a promise and don’t want to wait for its result, then we don’t need to add await to it.

For instance, we can write:

async function asyncFunc() {
  const writer = openFile('foo.txt');
  writer.writeLine('foo');
  writer.writeLine('bar');
  await writer.close();
}

We didn’t add await to the writer.writeLine method calls because we don’t want to wait for the result.

This way, we can make our function run faster since we don’t have to wait.

await is sequential, Promise.all() is parallel

We’ve to remember that await is run sequentially.

And Promise.all runs the array of promise we pass in in paralle.

So if we have:

async function foo() {
  const result1 = await promise1();
  const result2 = await promise2();
}

then await waits for each async function is fulfilled until the next line is run.

On the other hand, if we have:

async function foo() {
  const [result1, result2] = await Promise.all([
    promise1(),
    promise2(),
  ]);
}

then promise1 and promise2 are run in parallel.

Async Functions and Callbacks

await only affects the surrounding async function.

So an async function can’t await in a callback.

However, callbacks can be async functions.

This makes callbacks tricky to use.

For instance, we can’t use await in a map callback that isn’t async:

async function download(urls) {
  return urls.map(url => {
    const content = await makeRequest(url);
    return content;
  });
}

We don’t have an async keyword in front of the function.

But we also can’t write:

async function download(urls) {
  return urls.map(async url => {
    const content = await makeRequest(url);
    return content;
  });
}

since we won’t be able to get the results of each async function from the callback.

The awaiting is only done inside the callback.

What we should do is to map our URLs to promises and then call Promise.all on the array of promises.

For instance, we write:

async function download(urls) {
  const promiseArray = urls.map(async (url) => {
    const content = await makeRequest(url);
    return content;
  });
  return await Promise.all(promiseArray);
}

We call map to map the array of URLs to an array of promises, and then we return our Promise.all call to return a promise that resolves to the results of all the promises in an array.

Conclusion

Async functions are handy to use, but it can be misused if we aren’t careful.

So we should make sure to use them properly to avoid unexpected results.