Deno top level await
Asked Answered
L

2

7

Was reading the homepage of deno the new JS runtime

I saw the following code:

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" });
}

I never have seen the following syntax (for await):

for await (const req of s) {
  req.respond({ body: "Hello World\n" });
}

What is this kind of syntax?

Is it specific to deno or is it a top-level-await found in this tc39 proposal?

Edit: Why can it be used outside of an async function?

Libel answered 20/5, 2020 at 15:23 Comment(1)
B
13

for await...of statement is used to iterate through async iterators, and serve returns an async iterator, where each iteration will be a new incoming request.

Is it specific to deno or is it a top-level-await found in this tc39 proposal?

No, it's not specific to Deno and it's a different proposal than top-level await.


Here's a simple example of an asyncIterator that works in browsers too (non-deno exclusive)

const obj = {
   async *[Symbol.asyncIterator]() {
      for(let i = 0; i < 10; i++)
        yield new Promise(resolve => setTimeout(() => resolve(i), 100));
   }
};

(async() => {
  // You don't need the wrapper if the environment 
  // supports top-level await, e.g: Deno
  for await(const i of obj)
    console.log(`${i}`, new Date())

})();

Edit: Why can it be used outside of an async function?

Because Deno supports top-level await

Bianca answered 20/5, 2020 at 15:25 Comment(4)
Btw one interesting addition is that the code was run outside of an aync function in the exampleLibel
You're welcome. I added an example, so you can see how it's built.Bianca
Most browsers does not support top-level await, reason why I wrapped it inside an async function. But it was the perfect example to show it's not part of top-level awaitBianca
I think "serve returns an async iterator," part is not correct. It returns and asyncronous iterable object and only once invoked like s[Symbol.asyncIterator]() you receive an async iterator. The for await hides all these inner workings.Andromache
A
1

This also drew my attention when i decided to use Deno. I like Deno as it is native on ES6 like Promises, Import - Export and also you don't even need npm like in Node, just import the package from an url.

Now this async and await abstractions are ok for the sake of simplification of the code but it also hides many things, that some people basically have difficult time in understanding what exactly is going on.

I would first advise you to spare some time and read this beautiful article on asychronous iteration.

The thing is, in the very basic code to start a server that's given like

import { serve } from "https://deno.land/[email protected]/http/server.ts";
const s = serve({ port: 8000 });
console.log("http://127.0.0.1:8000/");

for await (const req of s) {
  req.respond({ body: "Hello World\n" });
}

you have to accept that for some reason the for await loop is turning once, when a request is received. Actually that's exactly what it does. The machine which drives the asychronous iteration and promises are hidden.

Now we should know that the s in;

const s = serve({ port: 8000 });

is in fact an asynchronous iterable object. So it has a method called [Symbol.asyncIterator]. When you invoke it like s[Symbol.asyncIterator]() you get an asynchronous iterator object with a next method. Normally with synch iterators when the next() is called you receive an object like {value: "stg", done: false} but in async iterators a Promise is received. This promise, once resolves when a request is received (or rejects if an error happens) gives us an object like

{ value: ServerRequest Object 
, done : false
}

So the above code can also be written like;

import { serve } from "https://deno.land/[email protected]/http/server.ts";
const s = serve({ port: 8000 });
console.log("http://127.0.0.1:8000/");

var rqs = s[Symbol.asyncIterator](),
    srv = (app,rqs) => rqs.next().then(rq => (app(rq.value), srv(app,rqs))),
    app = req => req.respond({body: "Hello World\n"});

srv(app,rqs); // receive requests and handle them with your app
Andromache answered 13/2, 2021 at 17:27 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.