Categories
Modern JavaScript

Best of Modern JavaScript — Regex u Flag and Timers

Since 2015, JavaScript has improved immensely.

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

In this article, we’ll look at new JavaScript regex features and timers.

The u Flag

The u flag is another flag that’s introduced with ES6.

It switches on Unicode mode for regexes.

The most can recognize Unicode point escape sequences like u{094F} in our regex.

Also, characters in the regex pattern and the string are also recognized as code points.

Code units are converted into code points.

In non-Unicode mode, a lone surrogate matches surrogate pair code points.

For example, if we have:

/uD83D/.test("uD83DuDC2B")

We get true returned.

With Unicode mode on, this will return false since the 2 code points should be put together to create one character.

So /uD83D/u.test(“uD83DuDC2B”) returns false .

Actual lone surrogates are still matched with the u flag on:

/uD83D/u.test("uD83D uD83DuDC2B")

So the expression above returns true .

This means that 2 code points together will be interpreted as the one correct character in Unicode mode.

The dor operator will match code points instead of code units.

For instance:

'uD83DuDC2B'.match(/./gu).length

returns .

But:

'uD83DuDC2B'.match(/./g).length

returns 2.

In Unicode mode, quantifiers apply to code points.

But in non-Unicode mode, they apply to single code units.

So if we have:

/uD83DuDC2B{2}/u.test("uD83DuDC2BuD83DuDC2B")

This will return true .

But:

/uD83DuDC2B{2}/.test("uD83DuDC2BuD83DuDC2B")

returns false .

flags Property

The flags property is a new property of a regex object.

It returns the flags that’s added to the regex.

For example, if we have:

/foo/gy.flags

We get “gy” .

We can’t change the flags of an existing regex.

But we can use the flags property to make a copy of a regex with the flags.

For instance, we can write:

const regex = /foo/gy
const copy = new RegExp(regex.source, regex.flags);

The source property has the regex pattern.

And flags has the flags.

RegExp Constructor

The RegExp constrictor is useful for copying regexes as we can see.

It takes the pattern and the flags.

String Methods that Delegate to Regex Methods

Some string instance methods delegate to regex instance methods.

They’re:

  • String.prototype.match calls RegExp.prototype[Symbol.match]
  • String.prototype.replace calls RegExp.prototype[Symbol.replace]
  • String.prototype.search calls RegExp.prototype[Symbol.search]
  • String.prototype.split calls RegExp.prototype[Symbol.split]

Async Programming

JavaScript has extensive async programming features.

Each browser tab runs in a single process called the event loop.

The loop runs browser-related things that’s fed via the task queue.

Tasks include parsing HTML, running JavaScript code, listening to user input, and more.

Timers

JavaScript has the setTimeout to delay the execution of a piece of code.

It takes a callback to run the code after a given delay.

The first argument is the callback to run.

And the 2nd is the number milliseconds after now to add the callback to the queue.

Once it’s called, the callback is added to the task queue.

The delay specifies when the callback is added, not when it actually runs.

This may happen later.

DOM Changes

DOM changes don’t happen immediately.

It happens every 16ms.

Conclusion

The u flag lets us enable Unicode mode to let us search for Unicode strings properly.

setTimeout lets us run code with a delay.

Categories
Modern JavaScript

Best of Modern JavaScript — Promises

Since 2015, JavaScript has improved immensely.

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

In this article, we’ll look at JavaScript promises.

Benefits of Promises

Promises are chained.

And they look similar to synchronous code.

For example, we can write:

asyncFunction(arg)  
  .then(result1 => {  
    console.log(result1);  
    return asyncFunction2(x, y);  
  })  
  .then(result2 => {  
    console.log(result2);  
  });

We return a promise in the then callback to let us call then again.

Chaining is also simple since we just call then until we’re done with running the promises we want.

Composing async calls is easier since we can do the chaining.

Error handling can be done with the catch method.

For instance, we can write:

asyncFunction(arg)  
  .then(result1 => {  
    console.log(result1);  
    return asyncFunction2(x, y);  
  })  
  .then(result2 => {  
    console.log(result2);  
  })  
  .catch(error => {  
    console.log(error);  
  })

The catch method takes a callback that has an error parameter.

So we can do whatever we want with them inside the callback.

The function signatures are clear and we can see the parameters easily.

Promises is also a standard way to write async code.

Before promises are introduced to ES6 as a native features, there’re many libraries that implement the same thing.

But now, ES6 makes promises standard so we can use it anywhere.

Creating Promises

To create a promise, we can use the Promise constructor.

For instance, we can write:

new Promise((resolve, reject) => {  
  setTimeout(() => resolve('DONE'), 100);  
});

We use the constructor with a function that takes the resolve and reject parameters.

resolve is a function that’s called to return the value when it succeeds.

reject is a function that’s called to return the reason why the promise fails.

A promise is a container for value and an event emitter.

Promises looks like synchronous code, but they’re non-blocking.

They can’t return anything synchronously.

ES6 introduced a way for us to suspend and resume code with generators.

Therefore, this is used as the foundation for promises and async functions in JavaScript.

For instance, if we have:”

async function main() {  
  const x = await asyncFunc();  
  console.log(x);  
  //...  
}  
main();

asyncFunc returns a promise and runs the console log after the function is called.

await is like yield in JavaScript generator functions.

It does the same thing. It pauses the code and then continues running when the result is retrieved.

Using Promises

Promises have 3 states.

They can be pending, fulfilled, or rejected.

Pending means that the result hasn’t been computed.

Fulfilled means the result was computed successfully.

Rejected means a failure occurred during computation.

The parameter we pass into the Promise constructor is called an executor.

In addition to calling reject to make a promise fail, we can also reject a promise with an exception.

Consuming a Promise

To consume a promise, we can call then to get the resolved result from a promise.

The callback we pass into then takes the resolved results of a promise.

catch takes a callback which takes the argument we passed into reject .

For example, we can write:

promise  
  .then(value => {  
    //...  
  })  
  .catch(error => {  
    //...  
  });

value has the resolved value of promise .

error has the rejected value.

then can also take 2 arguments.

One is the resolved callback and the 2nd argument is the rejected callback.

So we can also catch errors by writing:

promise.then(  
  null,  
  error => {  
    //...  
  });

The 2nd argument is the same as the catch callback and catches the error of the promise.

Conclusion

Promises have many benefits, and we can create and consume them in a standard way.

Categories
Modern JavaScript

Best of Modern JavaScript — Iterators and Iterables

Since 2015, JavaScript has improved immensely.

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

In this article, we’ll look at JavaScript iterable objects.

Iterators that are Iterable

We can move the next function into its own object method if we return this in the Symbol.iterator method.

For instance, we can write:

function iterate(...args) {
  let index = 0;
  const iterable = {
    [Symbol.iterator]() {
      return this;
    },
    next() {
      if (index < args.length) {
        return {
          value: args[index++]
        };
      } else {
        return {
          done: true
        };
      }
    },
  };
  return iterable;
}

We have the next within the iterable object we return.

The args have the items that we want to iterate over.

We return the object with the value while the index is less than args.length .

And we return an object with done set to true if there’s nothing left to iterate.

We return this inside the Symbo.iterator method so that the iteration can be done.

for-of loops work only with iterables and not the iterators directly.

Iterables always have the Symbol.iterator method, so we got to put our iterator over our iterable object to make it iterable.

return() and throw()

There’re 2 iterator methods that are optional.

return gives us an iterator the opportunity to clean if iterator ends early.

throw lets us forward a method call to a generator that’s iterated via yield* .

Closing Iterators

We can use return to close an iterator.

For instance, if we have the iterate function that we have before.

Then we can call break to end the for-of loop cleanly:

for (const x of iterate('foo', 'bar', 'baz')) {
  console.log(x);
  break;
}

return must return an object.

This is because of how generators handle the return statements.

Some constructors close iterators that aren’t completely clean up.

They include:

  • for-of
  • yield*
  • destructuring
  • Array.from()
  • Map(), Set(), WeakMap(), WeakSet()
  • Promise.all(), Promise.race()

Combinators

Combinators are functions that combine existing iterables to create new ones.

For example, we can create one by writing:

function combinator(n, iterable) {
  const iter = iterable[Symbol.iterator]();
  return {
    [Symbol.iterator]() {
      return this;
    },
    next() {
      if (0 < n) {
        n--;
        return iter.next();
      } else {
        return {
          done: true
        };
      }
    }
  };
}

We create a function to return the first n items from the iterable object.

Then we can use it by writing:

const arr = ['foo', 'bar', 'baz', 'qux'];
for (const x of combinator(3, arr)) {
  console.log(x);
}

This will log the first 3 items of the arr array.

Infinite Iterables

Iterable can return an infinite amount of values.

For instance, we can write:

function evenNums() {
  let n = 0;
  return {
    [Symbol.iterator]() {
      return this;
    },
    next() {
      return {
        value: (++n) * 2
      };
    }
  }
}

to create an iterable object that returns even numbers.

Then we can create it by writing:

const nums = evenNums()
console.log(nums.next());
console.log(nums.next());
console.log(nums.next());

We call the evenNums function to create iterator.

Then we call next on each iterator to generate the numbers.

Conclusion

We can create iterable objects that return a finite and infinite number of values.

Categories
Modern JavaScript

Best of Modern JavaScript — Iterators and 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 JavaScript iterable objects and generator functions.

Clean Up of Generators

If we use break to break a loop, then we can clean up with the try-finally constructor.

For example, if we have:

function* genFn() {
  yield 'foo';
  yield 'bar';
  yield 'baz';
  console.log('clean up');

}

const gen = genFn();

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

Then the console log will run when the generator returned all the items.

However, if we use break in our loop:

for (const x of gen) {
  console.log(x);
  break;
}

Then the console log is never run.

We can run the cleanup step when we use break in our loop by wrapping the yield code with try-finally.

For example, we can write:

function* genFn() {
  try {
    yield 'foo';
    yield 'bar';
    yield 'baz';
  } finally {
    console.log('clean up');
  }
}

const gen = genFn();

for (const x of gen) {
  console.log(x);
  break;
}

We added the finally block, which will run when the break statement is run.

This is useful for running clean up code on our generator.

If we implement our own iterator, we can put the function in our iterator’s return method:

const obj = {
  [Symbol.iterator]() {
    function hasNextValue() {
      //...
    }

    function getNextValue() {
      //...
    }

    function cleanUp() {
      //...
    }

    return {
      next() {
        if (hasNextValue()) {
          const value = getNextValue();
          return {
            done: false,
            value: value
          };
        } else {
          //...
          return {
            done: true,
            value: undefined
          };
        }
      },
      return () {
        cleanUp();
      }
    };
  }
}

We have the return method included in the object we return with the Symbol.iterator method.

Closing Iterators

We can close any iterators by creating our own generator function to close it.

For instance, we can write:

function* take(n, iterable) {
  for (const x of iterable) {
    if (n <= 0) {
      break;
    }
    n--;
    yield x;
  }
}

We call break on the loop to end it with when the ending condition is met.

Generators

Generators are pieces of code that we can pause and resume.

It’s denoted by the function* keyword for generator functions.

yield is an operation that a generator used to pause itself.

Generators can receive input and send output with yield .

We can create a generator function by writing:

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

This returns a generator object which we can call next on to return the values that are yield:

const genObj = genFn();
console.log(genObj.next());
console.log(genObj.next());
console.log(genObj.next());

We call genFn to return a generator.

Then we call next to get each value sequentially.

So we get:

{value: "foo", done: false}
{value: "bar", done: false}
{value: "baz", done: false}

We return an object with the value and done property.

The value has the value from yield .

done tells us whether all the values have been yield from the generator.

Conclusion

We can clean up our generator by adding a try-finally block.

Also, generator functions create generators that let us return the value sequentially.

Categories
Modern JavaScript

Best of Modern JavaScript — 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 JavaScript iterable objects.

Speed of the Iteration Protocol

The speed of the iteration protocol has been taken into account when this is created.

Memory management is fast when managing small objects.

JavaScript engine optimization iteration so that no intermediate objects are allocated.

Reuse the Same Iterable Object

We can use iterable multiple times.

So we can write:

const results = [];
const iterator = [1, 2, 3][Symbol.iterator]();

while (!(val = iterator.next()).done) {
  results.push(val);
}

We get the iterator from the array.

And then we called it with our while loop to get the results.

Iteration

There’re rules that govern the JavaScript iteration protocol.

There are some rules for the next method.

As long as the iterator is returning a value next , returns an object with the value property.

And done will be false .

So we have:

{ value: x, done: false }

After the last value is iterated over, next should return an object whose property done is true .

Iterables that Return Iterators

Iterables can return fresh iterators or return the same iterator.

If they return fresh iterators, then each ones return values from the start.

So if we have something like and array and we use:

function getIterator(iterable) {
  return iterable[Symbol.iterator]();
}

to get the iterator, then we can compare them to see if they return the same iterator.

For instance, if we have:

const arr = ['a', 'b'];
console.log(getIterator(arr) === getIterator(arr));

Then the expression would log false .

This means even though the array is the same, they return the same iterator.

Other iterables like generators return the same iterator each time.

If we have generator objects, we return the same generator each time it’s called:

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

const gen = genFn();
console.log(getIterator(gen) === getIterator(gen));

The genFn is a generator function that returns a generator,

And when we get the iterator from the generator, we get the iterator from it and compare them and the expression logs true .

So the same generator has the same iterator.

We can iterate over a fresh iterator multiple times.

For instance, we can write:

const arr = ['foo', 'bar', 'baz'];

for (const a of arr) {
  console.log(a);
}

for (const a of arr) {
  console.log(a);
}

to loop through the same array twice.

On the other hand, if we have a generator:

function* genFn() {
  yield 'foo';
  yield 'bar';
}
const gen = genFn();

for (const a of gen) {
  console.log(a);
}

for (const a of gen) {
  console.log(a);
}

then we only loop through it once even if we have 2 loops.

Closing Iterators

There’re 2 ways that an iterator can be closed.

An iterator can be closed with exhaustion or closing.

Exhaustion is when the iterator returned all the iterable values.

Closing is done by calling return in the iterator function.

When we call return , then next won’t be called.

return is an optional method.

Not all iterators have it.

Iterators that have a return call is called closable.

return should only be called if an iterator hasn’t exhausted.

This can happen if we loop through the for-of loop with break , continue , return or throw .

return should produce an object that returns { done: true, value: x } .

Conclusion

Iterable objects can have different variations.

They can return a single iterator or multiple instances.

They can also be closable.