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?
Top level await You can use the await keyword on its own (outside of an async function) at the top level of a module. This means that modules with child modules that use await will wait for the child modules to execute before they themselves run, all while not blocking other child modules from loading.
Top-level await enables developers to use the await keyword outside of async functions. Starting from Node. js v14 top-level await is available and it is only available in ES modules. This means you can not use it with common js modules.
To use top-level await in TypeScript, you have to set “target” to es2017 or higher. The “module” option in “tsconfig. json” has to be set to esnext or system . Your code also has to run on Node.
The keyword Await makes JavaScript wait until the promise returns a result. It has to be noted that it only makes the async function block wait and not the whole program execution.
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
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
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With