ES2018 new features

  • Asynchronous iterators (this article)
  • Regular expression reverse (lookbehind) assertion
  • Regular expression Unicode escape
  • The template string for a non-escape sequence
  • Regular expression s/dotAll mode
  • Regular expressions name capture groups
  • Object expansion operator
  • Promise.prototype.finally

1. An overview of the

In ECMAScript 2015(ES6) JavaScript introduced an iterator interface for iterating through data. The iterator object knows how to access one item in the collection at a time and keep track of the current position in the sequence. In JavaScript, an iterator is an object that provides a next() method that returns the next item in a sequence. This method returns two properties: done and value.

Once the iterator object is created, next() can be called repeatedly.

function makeIterator(array) {
  let nextIndex = 0;  // Initial index

  // Returns an iterator object whose property is a next method
  return {
    next: function() {
      if (nextIndex < array.length) {
        // If the end is not reached, return the current value and increment the index by 1
        return { value: array[nextIndex++], done: false };
      }

      // At the end, the done attribute is true
      return {done: true}; }}; }Copy the code

Once initialized, the next() method can be used to access the keys in the object in turn:

const it = makeIterator(['j'.'u'.'s'.'t']);
it.next().value;  // j
it.next().value;  // u
it.next().value;  // s
it.next().value;  // t
it.next().value;  // undefined
it.next().done;   // true
it.next().value;  // undefined
Copy the code

2. Iterable

An object that defines iterative behavior, such as in for… What values are looped through in of. To be iterable, an object must implement the @@iterator method, which means that the object (or an object in its prototype chain) must have properties with symbol. iterator keys:

String, Array, TypedArray, Map, and Set all have iterables built in because their prototype objects have a symbol. iterator method.

const justjavac = {
  [Symbol.iterator]: (a)= > {
    const items = [`j`.`u`.`s`.`t`.`j`.`a`.`v`.`a`.`c`];
    return {
      next: (a)= > ({
        done: items.length === 0.value: items.shift()
      })
    }
  }
}
Copy the code

Once we have defined iterables, we can create iterables in array. from, for… Use this object in of:

[...justjavac];
// ["j", "u", "s", "t", "j", "a", "v", "a", "c"]

Array.from(justjavac)
// ["j", "u", "s", "t", "j", "a", "v", "a", "c"]

new Set(justjavac);
// {"j", "u", "s", "t", "a", "v", "c"}

for (const item of justjavac) {
  console.log(item)
}
// j 
// u 
// s 
// t 
// j 
// a 
// v 
// a 
// c
Copy the code

3. Synchronize iteration

Since the next value in the sequence and the “done” state of the data source must be known when the iterator method returns, iterators are only suitable for representing synchronous data sources.

While many data sources encountered by JavaScript programmers are synchronous (such as in-memory lists and other data structures), many others are not. For example, any data source that requires I/O access is typically represented using an event-based or streaming asynchronous API. Unfortunately, iterators cannot be used to represent such data sources.

(Even the promise iterator is not sufficient because its value is asynchronous, but the iterator needs to determine the “done” state synchronously.)

To provide a common data access protocol for asynchronous data sources, we introduce the AsyncIterator interface, for-await-of and asynchronous generator functions.

4. Asynchronous iterators

An asynchronous iterator is just like an iterator, except that its next() method returns a {value, done} promise. As mentioned above, we must return the promise of the iterator result because the iterator’s next value and “complete” state may not be known when the iterator method returns.

Let’s modify the previous code:

 const justjavac = {
- [Symbol.iterator]: () => {
+ [Symbol.asyncIterator]: () => {
     const items = [`j`, `u`, `s`, `t`, `j`, `a`, `v`, `a`, `c`];
     return {
- next: () => ({
+ next: () => Promise.resolve({
         done: items.length = = = 0,
         value: items.shift()
       })
     }
   }
 }
Copy the code

Ok, we now have an asynchronous iterator that looks like this:

const justjavac = {
  [Symbol.asyncIterator]: (a)= > {
    const items = [`j`.`u`.`s`.`t`.`j`.`a`.`v`.`a`.`c`];
    return {
      next: (a)= > Promise.resolve({
        done: items.length === 0.value: items.shift()
      })
    }
  }
}
Copy the code

We can iterate with the following code:

for await (const item of justjavac) {
  console.log(item)
}
Copy the code

If you encounter a SyntaxError: for await (… of …) Is only valid in async functions and async generators error, because for-await-of can only be used in async functions or async generators.

Modify:

(async function(){
  for await (const item of justjavac) {
    console.log(item)
  }
})();
Copy the code

5. Synchronous iterators vs. asynchronous iterators

5.1 Iterators

/ / the iterator
interface Iterator {
    next(value) : IteratorResult;
    [optional] throw(value) : IteratorResult;
    [optional] return(value) : IteratorResult;
}

// The result of iteration
interface IteratorResult {
    value : any;
    done : bool;
}
Copy the code

5.2 Async Iterators

// Asynchronous iterator
interface AsyncIterator {
    next(value) : Promise<IteratorResult>;
    [optional] throw(value) : Promise<IteratorResult>;
    [optional] return(value) : Promise<IteratorResult>;
}

// The result of iteration
interface IteratorResult {
    value : any;
    done : bool;
}
Copy the code

6. asynchronous generator functions

Asynchronous generator functions are similar to generator functions, but with the following differences:

  • When called, the asynchronous generator function returns an object, “async Generator “, with three methods (next.throw, andreturn), each method returns a Promise, and the Promise returns{ value, done }. A normal generator function does not return a Promise, but returns it directly{ value, done }. This automatically makes the returned asynchronous generator object iterative asynchronously.
  • Allows the use ofawaitExpressions andfor-await-ofStatements.
  • To modify theyield*To support asynchronous iteration.

Example:

async function* readLines(path) {
  let file = await fileOpen(path);

  try {
    while(! file.EOF) {yield awaitfile.readLine(); }}finally {
    awaitfile.close(); }}Copy the code

The async generator function returns an async generator object that can be used in for-await-of statements.

7. To achieve

  • Chakra – Not currently supported
  • JavaScriptCore – Safari Tech Preview 40
  • SpiderMonkey – Firefox 57
  • V8 – Chrome 63

Polyfills

Facebook’s Regenerator project provides a polyfill for the AsyncIterator interface, turning asynchronous generator functions into ECMAScript 5 functions that return AsyncIterator objects. Regenerator also does not support for-await-of asynchronous iterative syntax.

The Babylon Parser project supports asynchronous generator functions and for-await-of statements (v6.8.0+). You can use its asyncGenerators plugin.

require("babylon").parse("code", {
  sourceType: "module".plugins: [
    "asyncGenerators"]});Copy the code

Additionally, as of 6.16.0, async iteration is included in Babel under the the name “babel-plugin-transform-async-generator-functions” as well as with babel-preset-stage-3. Note that the semantics implemented there are slightly out of date compared to the current spec text in various edge cases.

Also, as of 6.16.0, asynchronous iterations are included under “babel-plugin-transform-async-generator-functions” of Babel and babel-preset-stage-3.

require("babel-core").transform("code", {
  plugins: [
    "transform-async-generator-functions"]});Copy the code