One of the hottest things on the front end these days is Ry (Ryan Dahl) ‘s new project deno, with many IT news and media outlets using the headline “Next Generation Node.js”. I read deno’s source code this weekend and wrote this article specially. Long warning (5000 words, 11 pictures).
0. Why was Deno developed?
This is a diagram I made last week that gives a brief history of JavaScript. It was just modified to add annotations for the release dates of Node.js and Deno. Node.js and Deno were non-browser JavaScript runtimes developed by Ryan Dahl in 2009 and 2018, respectively, based on the latest front-end technology of the year.
Ryan Dahl didn’t develop deno “just for fun” or to replace Node. I’ll explain it slowly.
1. Right now deno is just a demo
I spent two days looking at the source code of deno (fortunately, it is in the initial stage, the source code is very few and easy to understand), along with all the issues and PR. I don’t know how “it can be considered the next generation of Node” is imagined.
Node.js is the creator of node.js. The author replied:
The main difference is that Node works and Deno does not work : )
The biggest difference is that Node works and Deno doesn’t:)
Right now Deno is just a Demo, not even a binary distribution. Fortunately, compiling from source is easier (if you’re not using Windows).
At the high-level level, Deno provides a V8 binding to the system API as simple as possible. The reason for using Golang instead of C++ is that it makes it easier to add new features like http2 compared to Node.
The author does not answer why Rust was not chosen.
Let’s compare the startup performance of the two. Run separately:
console.log('Hello world')
Copy the code
I wrote earlier about new plans for Node.js: 8 times faster startup with V8 snapshot. What if we compile Node.js with –without-snapshot?
Still, it’s a big difference, since deno needs to load a TypeScript compiler. After all, it is a demo version, I hope to optimize it later.
Another way to improve performance is to use LLVM as a back-end compiler to compile TypeScript code into WebAssembly and run it in V8, or even to compile the source code directly into binary code. Ryan Dahl says deno only needs one compiler, TS. But since deno is browser-compatible, WebAssembly should be supported as well.
Deno can cache ts compilation results (~/.deno/cache), so the current concerns are startup speed and initial compilation speed.
Or build it before release, and deno is no longer intended for development. Deno is a TS runtime, so it should be possible to run TS code directly. If TS is compiled to JS in advance, deno falls back to the JS runtime.
2. Should beginners learn Node.js or Deno?
Ryan Dahl’s answer to this question was neat:
Use Node. Deno is a prototype / experiment.
Use the Node. Deno is only a prototype or experimental product.
As you can see from the introduction, the goal of Deno is not to be Node compatible, but browser compatible.
So, Deno is not replacing Node.js, nor is it the next generation of Node.js, nor is it abandoning NPM to rebuild the Node ecosystem. Deno’s current goal is to embrace the browser ecosystem.
I have to say that’s a great goal. Ryan Dahl developed Node.js, and the community built the entire NPM ecosystem. In another answer to JustJavac: What exactly is NodeJS in the eyes of pure front-end development? “Node.js is one of the pillars of front-end engineering,” it says.
While Ryan Dahl left Node.js to go to the Golang community, Now Ryan Dahl is back, bringing Golang to the JavaScript community, developing Deno, and embracing the browser ecosystem. 👍
Let’s look at deno’s goals for the Web API:
- High level
- The Console)
- File/FileList/FileReader/Blob
- XMLHttpRequest
- WebSocket
- Middle level
- AudioContext/AudioBuffer
- Canvas
There will even be webGL and GPU support.
3. Deno architecture
Parsa Ghadimi drew a diagram of Deno’s architecture:
The underlying layer uses v8Worker2 developed by the authors, while event-loop is based on the PUB/SUB model. About v8worker can take a look at this PPT:docs.google.com/presentatio…
I’m curious that Deno uses Protobuf instead of Mojo. Since the goal was to be browser-compatible and not use Mojo, but rebuild the wheel on protobuf, Ryan Dahl was the real wheel. Mojo is a shortcut if you want to be compatible with the browser ecosystem, and a non-serialized zero-Copy library should be used if the goal is a high-performance server. – Protobuf doesn’t seem to be a good fit for Deno either way. From the issue, Ryan Dahl had never heard of Mojo before, but after seeing Mojo, he still felt that protobuf was the right choice.
Mojo is a new IPC mechanism developed by Google to replace the old Chrome IPC. The latest version of Chrome is currently 67, and Google plans to replace all the old IPC with Mojo in the 2019 version of 75.
– Mojo does have a similar approach to Protobuf, after all, it’s Google. The old IPC system was implemented based on a named Channel (IPC::Channel) between two processes (threads). The pipe is a queue, and IPC messages between processes are delivered in a first-in, first-out order, so there is a dependency on the order in which different IPC messages are sent. Mojo, in contrast, creates a separate message pipeline for each interface, ensuring that the IPC of the different interfaces is independent. It is also not expensive to create a separate message pipeline for the interface, with only a small amount of heap memory allocated.
Mojo architecture design:
Take a look at some of the architectural changes Chrome has made since Mojo was introduced.
Before:
After:
Is it a little bit of a microservice?
This dependency inversion is obvious to anyone familiar with Spring in Java. Blink was originally the lowest level typesetting engine in the browser, but with Mojo, Blink became the intermediate module. The recently popular Flutter is also based on Mojo architecture.
4. TypeScript VS JavaScript
Deno’s introduction is a secure TypeScript runtime environment. But if we look at the source code, deno is integrated into a TypeScript compiler, and ry/deno: main.go is in the entry file
// It's up to library users to call
// deno.Eval("deno_main.js", "denoMain()")
func Eval(filename string, code string) {
err := worker.Load(filename, code)
exitOnError(err)
} // It's up to library users to call
// deno.Eval("deno_main.js", "denoMain()")
func Eval(filename string, code string) {
err := worker.Load(filename, code)
exitOnError(err)
}
Copy the code
The deno_main.js file running with V8. JavaScript, not TypeScript.
We know from the previous analysis that this affects the initial startup speed of the deno. What about speed of execution? In theory, TypeScript being a statically typed language makes compiled JavaScript code faster to execute. As I mentioned earlier in “Front-end Programmers should Know SOMETHING about V8” V8 has a Type feedback feature for improving JavaScript performance.
When V8 executes a function, it does just-in-time compilation (JIT) based on the arguments passed to the function (arguments, not parameters, since JavaScript parameters are typed) :
However, when a function is later called with a different type, V8 Deopt the function.
(Remove the result of previous optimization, called “de-optimization”)
However, if we use TypeScript, all parameters are annotated by type, thus preventing the V8 engine from performing optimization operations internally.
5. Prospect and conjecture of denO performance
Although TypeScript avoids the de-tuning operations of V8, V8 executes ts compilation, and as you can see from bytecode or machine code, V8 still generates code for Type Check, which checks the Type of arguments before calling a function. That is, although TypeScript guarantees the type of a function’s arguments, V8 cannot determine the type of a function’s arguments when compiled into JavaScript. It can only be checked before each call.
Second, when V8 encounters a function definition, it does not know the type of the argument. V8 can only determine the type of the function after it is called, and only then will V8 compile the function immediately. Here’s another paradox: typescript already knows the types of parameters when a function is defined, whereas V8 optimizes the types of arguments only when a function is called.
So, there are still a lot of problems with deno’s architecture, it’s just a demo. There are many directions that can be optimized for the future.
V8 is a JavaScript runtime, while deno is defined as a “safe TypeScript runtime”, at least on the current architecture, there is a significant performance penalty. However, there is no TypeScript runtime, and the next best thing is to put a TypeScript compiler in front of V8.
The execution process is like this:
Although I don’t use TypeScript in my projects, most of the third-party libraries I write in my projects provide a D.ts file. The biggest use of TypeScript today is in development and maintenance.
One approach we came up with was to fork a copy of V8’s source code and integrate the compilation process into it. TypeScript also requires an AST to compile into JavaScript and then generate JS code. V8 executes JS code by parsing an AST and generating intermediate code (ByteCode) based on the AST. Runtime performance would be improved if TypeScript could directly generate the bytecode for use.
Ryan Dahl probably wouldn’t have done that. But not necessarily, because the community already compiles a subset of TypeScript to WebAssembly.
While Microsoft’s JScript and VBScript have been losing ground to JavaScript, TypeScript is gaining momentum. Although compatibility with the ES specification has hampered TypeScript’s growth, it is expected that Microsoft will either provide a TS runtime or add support for the TS runtime in Chakra.
6. Summary
Regardless, deno is a great project, but it’s not “the next generation of Node.js.”
PS: Yesterday Ryan Dahl gave a talk on Design Mistakes in Node at JS Conf. So far there is only PPT, no Youtube video. Eight years earlier, In 2009, Ryan Dahl gave a talk at the JS Conf that gave birth to Node.js.
-
PPT:tinyclouds.org/jsconf.pdf
-
YouTube: Introduction to Node.js with Ryan Dahl