Original address: https://deno.land/v1

2020-05-13

Ryan Dahl, Bert Belder, and Bartek Iwańczuk

Dynamic languages are useful tools. Scripting allows users to connect complex systems together and express ideas quickly and succinctly without having to worry about details like memory management or building systems. In recent years, programming languages like Rust and Go have made it easier to produce complex native code. These projects are extremely important developments in computer infrastructure. However, it’s still important that we claim to have a strong scripting environment that addresses a variety of problem areas.

JavaScript is the most widely used dynamic language and runs on every device through a Web browser. A large number of programmers are proficient in JavaScript and have already put a lot of effort into optimizing its execution. Through standards bodies such as ECMA International, the language has been carefully and continuously improved. We believe JavaScript is a natural choice for dynamic language tools; Either in the browser environment or as a standalone process.

Node.js, our original venture in the field, has proved to be a very successful software platform. It has been found useful for building Web development tools, building standalone Web servers, and many other use cases. However, Node was designed in 2009, when JavaScript was a very different language. By necessity, Node had to invent concepts that were later adopted by standards bodies and added to the language in different ways. This is discussed in more detail in the “Design Mistakes in Node” presentation. Because of Node’s large user base, the system is difficult and slow to develop.

With the ever-changing JavaScript language and new features like TypeScript, building Node projects can become a daunting task, involving managing build systems and other onerous tools that take the fun out of dynamic language scripting. In addition, the mechanism for linking to external libraries is essentially centralized through the NPM repository, which is not ideal for the Web.

We believe that JavaScript and the surrounding software infrastructure have changed enough to merit simplification. We’re looking for a fun and efficient scripting environment that can handle multiple tasks.

1. A Web Browser for command-line Scripts

Deno is a new runtime for executing JavaScript and TypeScript outside of a Web browser.

Deno tries to provide a stand-alone tool for quickly scripting complex features. Deno is (and always will be) a single executable. Just like a web browser, it knows how to get external code. In Deno, a single file can define arbitrarily complex behavior without the need for any other tools.

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

Here a complete HTTP server module is added as a dependency in a single line. There are no additional configuration files, there is no install to do beforehand, just deno run example.js.

Here, a complete HTTP server module is added as a dependency on one line. No additional configuration files, no installation, just deno run example.js.

As with browsers, code is executed in a secure sandbox by default. Scripts cannot access hard drives, open network connections, or perform any other potentially malicious operations without permission. The browser provides apis for accessing cameras and microphones, but the user must first grant permission. Deno provides similar behavior in terminals. The above example will fail unless you provide the –allow-net command line flag.

Deno is careful not to deviate from standard browser JavaScript apis. Of course, not every browser API is Deno related, but wherever they are, Deno doesn’t deviate from the standard.

2. First Class TypeScript Support

We want Deno to be applicable to a wide range of problem domains: from small single-line scripts to complex server-side business logic. As programs become more complex, it becomes more important to have some form of type checking. TypeScript is an extension of the JavaScript language that allows users to optionally provide type information.

Deno doesn’t need other tools to support TypeScript. The runtime is designed with TypeScript in mind. The deno types command provides type declarations for everything deno provides. Deno’s standard modules are all written in TypeScript.

3. Promises All The Way Down

Node was designed before JavaScript had Promises and async/await concepts. Promises to Node are EventEmitter, which is based on important apis, sockets and HTTP. In addition to the engineering benefits of async/await, the EventEmitter mode has the problem of back pressure. Take TCP Sockets as an example. Sockets emit “data” events when receiving incoming packets. These “data” callbacks will be issued in an unrestricted manner, filling the process with events. Because Node continues to receive new data events, the underlying TCP sockets do not have the appropriate backpressure, so the remote sender is unaware that the server is overloaded and continues to send data. To alleviate this problem, the pause() method was added. This solves the problem, but requires extra code; And because flooding only occurs when the process is very busy, many Node applications can be flooded with data. The result is a long tail delay of the system.

In Deno, sockets are still asynchronous, but receiving new data requires the user to specify rean(). The proper construction of receive sockets does not require additional pause semantics. This is not unique to TCP Sockets. The lowest binding layer of the system is fundamentally associated with promises — we call these bindings “OPS.” All callbacks in Deno in one form or another came from Promise.

Rust has its own promise-like abstraction called Futures. Through the “op” abstraction, Deno makes it easy to bind the APIS of Rust Futures into JavaScript Promises.

4. Rust APIs

The main component we provide is the Deno command line interface (CLI). The CLI is version 1.0 today. But Deno is not a monolithic program, but is designed as a collection of Rust crates to allow integration at different layers.

Deno_core is a very simple version of Deno. It doesn’t rely on TypeScript or Tokio. It just provides our operational and resource infrastructure. That is, it provides a way to bind Rust Futures to JavaScript Promises. The CLI is, of course, entirely based on deno_core.

Rusty_v8 Crates provides high-quality Rust bindings for V8’s C ++ API. The API tries to match the original C ++ API as much as possible. This is zero-cost binding and the objects exposed in Rust are exactly the same as the objects you operate on in C ++. (For example, previous attempts at Rust V8 binding enforced persistence control.) The crate provides binaries built into the Github Actions CI, but it also allows users to compile V8 from scratch and tweak many of its build configurations. All V8 source code is distributed in a box. Finally, rusty_V8 tries to be a safe interface. It’s not 100% safe yet, but we’re getting there. Being able to interact with a VM as complex as V8 in a safe way was surprising and allowed us to find many difficult errors in Deno itself.

5. Stability

We guarantee a stable API in Deno. Deno has many interfaces and components, so it’s important to be transparent about what we mean by “stable.” Any JavaScript API we invent to interact with the operating system can be found in the “Deno” namespace (for example, deno.open ()). These have been carefully checked and we will not make backward incompatible changes to them.

All features that are not ready to be stabilized are hidden behind the “Unstable” command line flag. You can view documentation for unstable interfaces here. In future releases, some of these apis will also be stabilized.

In the global namespace, you’ll find various other objects (such as setTimeout() and fetch())). We’ve tried to make these interfaces look like the ones in the browser. But if we find unexpected incompatibilities, we will make changes. Browser standards define these interfaces, not us. All the corrections we publish are bug fixes, not interface changes. If it is incompatible with the browser’s standard APIS, you can correct the incompatibility prior to the major release.

Deno also has a number of Rust apis, namely deno_core and Rusty_V8 Crates. None of these apis is 1.0. We’ll continue to iterate on them.

6. Limited (Limitations)

It is important to understand that Deno is not a branch of Node – it is a completely new implementation. Deno has only been in development for two years, while Node has been in development for over a decade. Given the interest in Deno, we hope it will continue to grow and mature.

Deno may be a good choice today for some applications, but not yet for others. It will depend on the requirements. We want to be transparent about these restrictions to help people make informed decisions when considering using Deno.

6.1. Compatibility

Unfortunately, many users will find compatibility with existing JavaScript tools frustrating. In general, Deno is not compatible with Node (NPM) packages. At https://deno.land/std/node/ to set up a new compatibility layer, but is still far from complete.

Although Deno takes a tough approach to simplifying modular systems, in the end Deno and Node are very similar systems with similar goals. Over time, we expect Deno to be able to run more and more Node applications out of the box.

6.2 HTTP Server Performance (HTTP Server Performance)

We are constantly tracking the performance of Deno’s HTTP server. Hello-world’s Deno HTTP server processes about 25,000 requests per second with a maximum latency of 1.3 milliseconds. A comparable Node program processes 34,000 requests per second, with maximum latency between 2 and 300 milliseconds.

Deno’s HTTP server is implemented in TypeScript on native TCP Sockets. Node’s HTTP server is written in C and exposed as a high-level binding to JavaScript. We resisted the urge to add native HTTP server bindings to Deno because we needed to optimize the TCP Sockets layer and, more commonly, the OP interface.

6.3 TSC Bottleneck

Internally, Deno uses Microsoft’s TypeScript compiler to check for types and generate JavaScript. It is very slow compared to the time V8 takes to parse JavaScript. Early in the project, we had hoped that the “V8 Snapshot” would lead to significant improvements here. Snapshots certainly help, but it’s still slow. We certainly thought we could improve on the existing TypeScript compiler, but it was clear to us that ultimately we needed to implement type checking in Rust. It will be a difficult task and it will not happen soon. But it can provide an order of magnitude of performance improvement over the critical path experienced by developers. TSC must be ported to Rust. If you are interested in working together to solve this problem, please contact us.

6.4 Plugins/Extensions

We have a nascent plug-in system for extending the Deno runtime with custom actions. However, this interface is still under development and has been marked as unstable. As a result, accessing native systems other than those provided by Deno is difficult.

6.5 (Acknowledgements) thank you.

Many thanks to the many contributors who helped make this release possible. In particular: @kitsonk, which is instrumental in many parts of the system, including (but not limited to) the TypeScript compiler host, deno_typescript, deno packages, DENO installations, denO types, and stream implementations. @Kevinkassimo has contributed numerous bug fixes throughout the history of the project. His contributions include timer systems, TTY integration, and WASM support. Deno.maketempfile, deno.kill, deno.hostname, deno.realPath, STD/node require, window.queuemircoTask and REPL history. He also created the logo. @KT3K implements a continuous benchmark system (played a role in almost every major refactoring), signal handlers, permissions apis and many important bug fixes. @nayeemrmn has provided bug fixes in many parts of Deno, most notably greatly improving stack tracing and error reporting, and has been a great help in stabilizing the 1.0 API. @JustJavac has contributed a number of small but significant fixes to bring the denO API into line with Web standards, most notably writing the VS Code deno plug-in. @zekth contributes many modules to STD, including STD/Encoding/CSV, STD/Encoding/TOML, STD/HTTP/cookies and many other bug fixes. @axetroy helped fix all the beauty-related issues, fixed many bugs, and maintained VS Code plug-ins. @afinch7 implements the plug-in system. @keroxp implements the Websocket server and provides many bug fixes. @cknight provides lots of documentation and STD/node polyfills. @lucacasonato built almost the entire deno.land website. @hashrock has done a lot of amazing art, such as the loading page for doc.deno.land and the lovely picture at the top of that page!