3

A simple explanation of the "for await ...of" statement in Node.js - M...

 2 years ago
source link: https://www.mikealche.com/software-development/a-simple-explanation-of-the-for-await-of-statement-in-node-js?amp%3Butm_medium=rss&%3Butm_campaign=a-simple-explanation-of-the-for-await-of-statement-in-node-js
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

A simple explanation of the “for await …of” statement in Node.js

Hey there!

Some months ago Deno —a kind of successor to Node.js— was released and in the homepage a figured a little demo of how to use it:

import { serve } from "https://deno.land/[email protected]/http/server.ts";
const s = serve({ port: 8000 });
console.log("http://localhost:8000/");
for await (const req of s) {
  req.respond({ body: "Hello World\n" });
}

Suddenly my eyes went “What is that???” when looking at the await call after the for, but before the (const req of s) on line 4

I had never seen something like that and my first thought was “this is a pretty cool and weird thing that deno does”….

Imagine my surprise when upon reading more about deno I found out that that little piece of code it was in fact valid javascript and it was also valid in Node.js and I had no idea about it

So what is this? why have I never seen it? where should I use it? am I already missing out?

If you have the same questions, then good!

This post will try answering all those things!

First things first:

Iterators

Have you ever seen something like this?

class RandomNumberGenerator {
  [Symbol.iterator]() {
    return {
      next: () => {
        return { value: Math.random() };
      },
    };
  }
}

If you did, then great for you, you can skip to the next section!

If you haven’t, then let’s dive a bit into what this is doing:

This class RandomNumberGenerator is implementing the [Symbol.iterator] or @@iterator method (we refer to the method with the double @@ when the method is defined through a Symbol property).

The [Symbol.iterator] or @@iterator method when defined on an object allow the object to be iterated!

Since now we defined the @@iterator method as an instance method of the class RandomNumberGenerator, all the instances of this class will now be iterable. You can now test it by running the below code:

class RandomNumberGenerator {
  [Symbol.iterator]() {
    return {
      next: () => {
        return { value: Math.random() };
      },
    };
  }
}

const rand = new RandomNumberGenerator();

for (const random of rand) {
  console.log(random);
  if (random < 0.1) break;
}

In order for everything to work the @@iterator method must return an object that contains a next method AND that next method needs to return another object with the properties value and done.

value will contain the value returned, while done will be a boolean that if it’s set to true will end the iteration.

While value is mandatory, done can be omitted as in the above example (this allows us to define infinite iterables).

Ok cool!

We can now make things iterable!

When does this come as useful?

I believe it highly depends on the type of business logic that you are creating.

For example the Node.js Design Patterns book —which I highly recommend— gives an example of iterating the elements of a Matrix (which you’ve probably defined as an array of arrays).

Also I believe this article highlights some cool uses. It defines some pretty cool methods that seem very python-esque.

However if you want my honest and personal opinion I’ve yet to encounter a situation in which I say “This is a great use case for iterators”.

In any case, let’s go back to main theme of our article: what else do we need to add the await into that for loop?

Async Iterators

Async iterators are —as the name implies— the async version of what we’ve done in the above example.

Imagine that instead of returning a random number, we instead returned promises. How would that look like?

If we altered the above example to do that, it would look like this:

const simulateDelay = (val, delay) =>
  new Promise((resolve) => setTimeout(() => resolve(val), delay));

class RandomNumberGenerator {
  [Symbol.asyncIterator]() {
    return {
      next: async () => {
        return simulateDelay({ value: Math.random() }, 200); //return the value after 200ms of delay
      },
    };
  }
}

const rand = new RandomNumberGenerator();

(async () => {
  for await (const random of rand) {
    console.log(random);
    if (random < 0.1) break;
  }
})();

What were the changes?

  1. We first changed the Symbol property to be asyncIterator instead of just iterator
  2. We made the next method be an async function.
  3. I created the simulateDelay function which returns a promise that resolves a given value after a given amount of time.
  4. We added the await in the for loop
  5. We put the loop inside an Inmediately invoked function expression in order to not have problems with top-level await calls. (Note: this is no longer needed in Node.js version 14+)

And so we’ve made a simple program that is capable of iterating over an object that gets its elements in an async way.

If you know of any other implementation of async iterators outside the small deno example on the homepage please email them to me or comment them below!


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK