Link: deno.land/v1

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 generate complex native machine code. These projects are extremely important developments in computer infrastructure. However, we firmly believe that it is still important to have a strong scripting environment that can address 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. JavaScript is constantly improved through standards organizations like ECMA. We believe JavaScript is a natural choice for dynamic language tools, both in the browser environment and as a standalone process.

Our initial effort in this area, Node.js, 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. Out of 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 talk. Because of Node’s large user base, the system is difficult and slow to develop.

With the ever-changing nature of JavaScript and new gadgets like TypeScript, building Node projects can be a daunting task, as managing build systems and other onerous tools takes the fun out of dynamic language programming. In addition, NPM’s centralized package management mechanism does not fit the Web ideal.

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

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, you add the complete HTTP server module as a dependency on one line. There are no other configuration files and install is not required, just: deno run example.js.

As with browsers, code is executed in a secure sandbox by default. Without permission, Deno cannot access hardware, open network connections, or perform any other potentially malicious operations. 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 pass –allow-net.

Deno does not deviate from standardized browser JavaScript apis. Of course, not every browser API is Deno related, but Deno doesn’t deviate from the standard by any means.

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 selectively 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.

Promises All The Way Down

Node was designed before JavaScript had Promises or async/await concepts. EventEmitter in Node stands for Promises, and sockets and HTTP are based on it. In addition to the ergonomic benefits of async/await, the EventEmitter mode has the problem of back-pressure. Take the TCP socket as an example: The socket fires a Data event when it receives an incoming packet. These data callbacks will be issued in an unrestricted manner, flooding the entire process with events. Because Node continues to receive new data events, the underlying TCP socket does not have proper back-pressure, so the remote sender is unaware that the server is overloaded and continues to send data. To alleviate this problem, we added the pause() method. 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. As a result, the tail delay time of the system becomes very long.

In Deno, the socket is still asynchronous, but receiving new data requires the user to explicitly call read(). No additional pause() is required to construct the receive socket properly. This is not unique to TCP sockets. The lowest binding layer of the system is fundamentally about Promises — we call these bindings OPS. All the corrections in Deno in one form or another came from Promises.

Rust has its own abstract similar to Promises: Futures. With op abstraction, Deno makes it easy to bind Rust’s Future-base API into JavaScript Promises.

Rust APIs

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

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

Rusty_v8 crate 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 — the objects exposed in Rust are exactly the same as the objects you operate on in C ++. (For example, an earlier attempt to force a persistent handle in the Rust V8 binding.) This Crate provides binaries built into the Github Actions CI, but it also allows the user to compile V8 from scratch and adjust many of its build configurations. All V8 source code is distributed in Crate. 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 bugs in Deno itself.

Stability

We are committed to maintaining a stable API in Deno. Deno has many interfaces and components, so it is important to make what we call “stable” transparent. The JavaScript apis for Deno to interact with the operating system can be found in Denonamespace (for example, deno.open ()). We check them carefully and do not make backward incompatible changes to them.

Everything that is not yet stable is hidden behind the “Unstable” command line flag. You can view documentation for unstable interfaces here. In future releases, some of these apis will also become stable.

In the global namespace, you’ll find various other objects (such as setTimeout() and fetch()). We have tried to keep these interfaces consistent with those in the browser. But if we find an unexpected incompatibility, we will correct it. After all, it’s the browser standards that define these interfaces, not us. All the corrections we have issued are bug fixes, not interface changes. If it is incompatible with the browser standard API, we will correct it before the major release.

Deno also has many Rust apis, namely deno_core and Rusty_v8Crates. None of these apis has reached version 1.0 yet. We’ll continue to iterate on them.

Limitations

It’s important to understand that Deno is not a fork of Node — it’s 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.

Compatibility

Unfortunately, many users find Deno’s compatibility with existing JavaScript tools frustrating. In general, Deno is not compatible with Node (NPM) packages. Deno.land/STD /node/ is a new node compatibility layer, but there is still a lot of work to be done.

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.

HTTP Server Performance

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

Deno’s HTTP server is implemented in TypeScript on top of native TCP sockets. Node’s HTTP server is written in C and exposed as a high-level binding to JavaScript. We’ve been resisting the urge to add local HTTP server bindings to Deno because we want to optimize the TCP socket layer, or OP interface.

Deno is a better asynchronous server, with 25K requests per second being sufficient for most purposes. (If not, JavaScript may not be the best choice.) In addition, due to the widespread use of Promise (as described above), we expect Deno to generally exhibit better tail delays. Taken together, we do believe there are more performance benefits to the system, and we hope to achieve this in future releases.

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. This will be a difficult task and 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.

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 is marked as unstable. As a result, accessing native systems other than those provided by Deno is difficult.

Xietian.xyz /2020/05/14/…

Contact translator: [email protected]