- assert 断言
- async_hooks 异步钩子
- async_hooks/context 异步上下文
- buffer 缓冲区
- C++插件
- C/C++插件(使用 Node-API)
- C++嵌入器
- child_process 子进程
- cluster 集群
- CLI 命令行
- console 控制台
- Corepack 核心包
- crypto 加密
- crypto/webcrypto 网络加密
- debugger 调试器
- deprecation 弃用
- dgram 数据报
- diagnostics_channel 诊断通道
- dns 域名服务器
- domain 域
- Error 错误
- events 事件触发器
- fs 文件系统
- global 全局变量
- http 超文本传输协议
- http2 超文本传输协议 2.0
- https 安全超文本传输协议
- inspector 检查器
- Intl 国际化
- module 模块
- module/cjs CommonJS 模块
- module/esm ECMAScript 模块
- module/package 包模块
- net 网络
- os 操作系统
- path 路径
- perf_hooks 性能钩子
- permission 权限
- process 进程
- punycode 域名代码
- querystring 查询字符串
- readline 逐行读取
- repl 交互式解释器
- report 诊断报告
- sea 单个可执行应用程序
- stream 流
- stream/web 网络流
- string_decoder 字符串解码器
- test 测试
- timers 定时器
- tls 安全传输层
- trace_events 跟踪事件
- tty 终端
- url 网址
- util 实用工具
- v8 引擎
- vm 虚拟机
- wasi 网络汇编系统接口
- worker_threads 工作线程
- zlib 压缩
Node.js v20.18.0 文档
- Node.js v20.18.0
- 目录
-
导航
- assert 断言
- async_hooks 异步钩子
- async_hooks/context 异步上下文
- buffer 缓冲区
- C++插件
- C/C++插件(使用 Node-API)
- C++嵌入器
- child_process 子进程
- cluster 集群
- CLI 命令行
- console 控制台
- Corepack 核心包
- crypto 加密
- crypto/webcrypto 网络加密
- debugger 调试器
- deprecation 弃用
- dgram 数据报
- diagnostics_channel 诊断通道
- dns 域名服务器
- domain 域
- Error 错误
- events 事件触发器
- fs 文件系统
- global 全局变量
- http 超文本传输协议
- http2 超文本传输协议 2.0
- https 安全超文本传输协议
- inspector 检查器
- Intl 国际化
- module 模块
- module/cjs CommonJS 模块
- module/esm ECMAScript 模块
- module/package 包模块
- net 网络
- os 操作系统
- path 路径
- perf_hooks 性能钩子
- permission 权限
- process 进程
- punycode 域名代码
- querystring 查询字符串
- readline 逐行读取
- repl 交互式解释器
- report 诊断报告
- sea 单个可执行应用程序
- stream 流
- stream/web 网络流
- string_decoder 字符串解码器
- test 测试
- timers 定时器
- tls 安全传输层
- trace_events 跟踪事件
- tty 终端
- url 网址
- util 实用工具
- v8 引擎
- vm 虚拟机
- wasi 网络汇编系统接口
- worker_threads 工作线程
- zlib 压缩
- 其他版本
域#
¥Domain
¥Stability: 0 - Deprecated
源代码: lib/domain.js
该模块正在等待弃用。一旦替代 API 完成,则该模块将被完全弃用。大多数开发者不应该有理由使用这个模块。绝对必须拥有域提供的功能的用户可能暂时依赖它,但预计将来必须迁移到不同的解决方案。
¥This module is pending deprecation. Once a replacement API has been finalized, this module will be fully deprecated. Most developers should not have cause to use this module. Users who absolutely must have the functionality that domains provide may rely on it for the time being but should expect to have to migrate to a different solution in the future.
域提供了一种将多个不同的 IO 操作作为一组来处理的方法。如果任何注册到域的事件触发器或回调触发 'error'
事件,或抛出错误,则将通知域对象,而不是丢失 process.on('uncaughtException')
句柄中的错误上下文,或导致程序立即使用错误码退出。
¥Domains provide a way to handle multiple different IO operations as a
single group. If any of the event emitters or callbacks registered to a
domain emit an 'error'
event, or throw an error, then the domain object
will be notified, rather than losing the context of the error in the
process.on('uncaughtException')
handler, or causing the program to
exit immediately with an error code.
警告:不要忽视错误!#
¥Warning: Don't ignore errors!
发生错误时,域错误句柄不能替代关闭进程。
¥Domain error handlers are not a substitute for closing down a process when an error occurs.
根据 throw
在 JavaScript 中的工作方式的本质,几乎没有任何方法可以在不泄漏引用或创建某种其他类型的未定义的脆弱状态的情况下安全地 "从停止的地方继续"。
¥By the very nature of how throw
works in JavaScript, there is almost
never any way to safely "pick up where it left off", without leaking
references, or creating some other sort of undefined brittle state.
响应抛出的错误最安全的方法是关闭进程。但是,在正常的 web 服务器中,可能有很多打开的连接,因为别人触发了错误而突然关闭这些连接是不合理的。
¥The safest way to respond to a thrown error is to shut down the process. Of course, in a normal web server, there may be many open connections, and it is not reasonable to abruptly shut those down because an error was triggered by someone else.
更好的方法是向触发错误的请求发送错误响应,同时让其他人在正常时间完成,并停止在该工作进程中监听新的请求。
¥The better approach is to send an error response to the request that triggered the error, while letting the others finish in their normal time, and stop listening for new requests in that worker.
通过这种方式,domain
的使用与集群模块齐头并进,因为当工作进程遇到错误时,主进程可以衍生新的工作进程。对于扩展到多台机器的 Node.js 程序,终止的代理或服务仓库可以记录故障,并做出相应的反应。
¥In this way, domain
usage goes hand-in-hand with the cluster module,
since the primary process can fork a new worker when a worker
encounters an error. For Node.js programs that scale to multiple
machines, the terminating proxy or service registry can take note of
the failure, and react accordingly.
例如,这不是一个好主意:
¥For example, this is not a good idea:
// XXX WARNING! BAD IDEA!
const d = require('node:domain').create();
d.on('error', (er) => {
// The error won't crash the process, but what it does is worse!
// Though we've prevented abrupt process restarting, we are leaking
// a lot of resources if this ever happens.
// This is no better than process.on('uncaughtException')!
console.log(`error, but oh well ${er.message}`);
});
d.run(() => {
require('node:http').createServer((req, res) => {
handleRequest(req, res);
}).listen(PORT);
});
通过使用域的上下文,以及将我们的程序分成多个工作进程的弹性,我们可以做出更适当的反应,并以更高的安全性处理错误。
¥By using the context of a domain, and the resilience of separating our program into multiple worker processes, we can react more appropriately, and handle errors with much greater safety.
// Much better!
const cluster = require('node:cluster');
const PORT = +process.env.PORT || 1337;
if (cluster.isPrimary) {
// A more realistic scenario would have more than 2 workers,
// and perhaps not put the primary and worker in the same file.
//
// It is also possible to get a bit fancier about logging, and
// implement whatever custom logic is needed to prevent DoS
// attacks and other bad behavior.
//
// See the options in the cluster documentation.
//
// The important thing is that the primary does very little,
// increasing our resilience to unexpected errors.
cluster.fork();
cluster.fork();
cluster.on('disconnect', (worker) => {
console.error('disconnect!');
cluster.fork();
});
} else {
// the worker
//
// This is where we put our bugs!
const domain = require('node:domain');
// See the cluster documentation for more details about using
// worker processes to serve requests. How it works, caveats, etc.
const server = require('node:http').createServer((req, res) => {
const d = domain.create();
d.on('error', (er) => {
console.error(`error ${er.stack}`);
// We're in dangerous territory!
// By definition, something unexpected occurred,
// which we probably didn't want.
// Anything can happen now! Be very careful!
try {
// Make sure we close down within 30 seconds
const killtimer = setTimeout(() => {
process.exit(1);
}, 30000);
// But don't keep the process open just for that!
killtimer.unref();
// Stop taking new requests.
server.close();
// Let the primary know we're dead. This will trigger a
// 'disconnect' in the cluster primary, and then it will fork
// a new worker.
cluster.worker.disconnect();
// Try to send an error to the request that triggered the problem
res.statusCode = 500;
res.setHeader('content-type', 'text/plain');
res.end('Oops, there was a problem!\n');
} catch (er2) {
// Oh well, not much we can do at this point.
console.error(`Error sending 500! ${er2.stack}`);
}
});
// Because req and res were created before this domain existed,
// we need to explicitly add them.
// See the explanation of implicit vs explicit binding below.
d.add(req);
d.add(res);
// Now run the handler function in the domain.
d.run(() => {
handleRequest(req, res);
});
});
server.listen(PORT);
}
// This part is not important. Just an example routing thing.
// Put fancy application logic here.
function handleRequest(req, res) {
switch (req.url) {
case '/error':
// We do some async stuff, and then...
setTimeout(() => {
// Whoops!
flerb.bark();
}, timeout);
break;
default:
res.end('ok');
}
}
Error
对象的添加#
¥Additions to Error
objects
每当 Error
对象通过域路由时,都会向其中添加一些额外的字段。
¥Any time an Error
object is routed through a domain, a few extra fields
are added to it.
-
error.domain
最先处理错误的域。¥
error.domain
The domain that first handled the error. -
error.domainEmitter
触发带有错误对象的'error'
事件的事件触发器。¥
error.domainEmitter
The event emitter that emitted an'error'
event with the error object. -
error.domainBound
绑定到域的回调函数,并将错误作为其第一个参数传递。¥
error.domainBound
The callback function which was bound to the domain, and passed an error as its first argument. -
error.domainThrown
一个布尔值,指示错误是被抛出、触发还是传递给绑定的回调函数。¥
error.domainThrown
A boolean indicating whether the error was thrown, emitted, or passed to a bound callback function.
隐式绑定#
¥Implicit binding
如果正在使用域,则所有新的 EventEmitter
对象(包括 Stream 对象、请求、响应等)将在创建时隐式绑定到活动域。
¥If domains are in use, then all new EventEmitter
objects (including
Stream objects, requests, responses, etc.) will be implicitly bound to
the active domain at the time of their creation.
此外,传递给底层事件循环请求(例如 fs.open()
或其他回调获取方法)的回调将自动绑定到活动域。如果它们抛出,则域将捕获错误。
¥Additionally, callbacks passed to low-level event loop requests (such as
to fs.open()
, or other callback-taking methods) will automatically be
bound to the active domain. If they throw, then the domain will catch
the error.
为了防止过多的内存使用,Domain
对象本身没有被隐式地添加为活动域的子域。如果是这样,则阻止请求和响应对象被正确地垃圾收集就太容易了。
¥In order to prevent excessive memory usage, Domain
objects themselves
are not implicitly added as children of the active domain. If they
were, then it would be too easy to prevent request and response objects
from being properly garbage collected.
要将 Domain
对象嵌套为父 Domain
的子对象,则必须显式地添加它们。
¥To nest Domain
objects as children of a parent Domain
they must be
explicitly added.
隐式的绑定路由向 Domain
的 'error'
事件抛出错误和 'error'
事件,但没有在 Domain
上注册 EventEmitter
。隐式的绑定只处理抛出的错误和 'error'
事件。
¥Implicit binding routes thrown errors and 'error'
events to the
Domain
's 'error'
event, but does not register the EventEmitter
on the
Domain
.
Implicit binding only takes care of thrown errors and 'error'
events.
显式绑定#
¥Explicit binding
有时,使用的域不是应该用于特定事件触发器的域。或者,事件触发器可以在域的上下文中创建,但可以绑定到其他域。
¥Sometimes, the domain in use is not the one that ought to be used for a specific event emitter. Or, the event emitter could have been created in the context of one domain, but ought to instead be bound to some other domain.
例如,可能有一个域用于 HTTP 服务器,但也许我们希望为每个请求使用单独的域。
¥For example, there could be one domain in use for an HTTP server, but perhaps we would like to have a separate domain to use for each request.
这可以通过显式绑定来实现。
¥That is possible via explicit binding.
// Create a top-level domain for the server
const domain = require('node:domain');
const http = require('node:http');
const serverDomain = domain.create();
serverDomain.run(() => {
// Server is created in the scope of serverDomain
http.createServer((req, res) => {
// Req and res are also created in the scope of serverDomain
// however, we'd prefer to have a separate domain for each request.
// create it first thing, and add req and res to it.
const reqd = domain.create();
reqd.add(req);
reqd.add(res);
reqd.on('error', (er) => {
console.error('Error', er, req.url);
try {
res.writeHead(500);
res.end('Error occurred, sorry.');
} catch (er2) {
console.error('Error sending 500', er2, req.url);
}
});
}).listen(1337);
});
domain.create()
#
类:Domain
#
¥Class: Domain
-
¥Extends: <EventEmitter>
Domain
类封装了路由错误和未捕获异常到活动 Domain
对象的功能。
¥The Domain
class encapsulates the functionality of routing errors and
uncaught exceptions to the active Domain
object.
要处理其捕获的错误,则监听其 'error'
事件。
¥To handle the errors that it catches, listen to its 'error'
event.
domain.members
#
已显式地添加到域的定时器和事件触发器数组。
¥An array of timers and event emitters that have been explicitly added to the domain.
domain.add(emitter)
#
-
emitter
<EventEmitter> | <Timer> 要添加到域的触发器或定时器¥
emitter
<EventEmitter> | <Timer> emitter or timer to be added to the domain
显式地添加触发器到域中。如果触发器调用的任何事件句柄抛出错误,或者触发器触发 'error'
事件,则它将被路由到域的 'error'
事件,就像隐式绑定一样。
¥Explicitly adds an emitter to the domain. If any event handlers called by
the emitter throw an error, or if the emitter emits an 'error'
event, it
will be routed to the domain's 'error'
event, just like with implicit
binding.
这也适用于从 setInterval()
和 setTimeout()
返回的定时器。如果其回调函数抛出异常,则其将被域 'error'
句柄捕获。
¥This also works with timers that are returned from setInterval()
and
setTimeout()
. If their callback function throws, it will be caught by
the domain 'error'
handler.
如果定时器或 EventEmitter
已绑定到某个域,则将其从该域中删除,并改为绑定到该域。
¥If the Timer or EventEmitter
was already bound to a domain, it is removed
from that one, and bound to this one instead.
domain.bind(callback)
#
-
callback
<Function> 回调函数¥
callback
<Function> The callback function -
返回:<Function> 绑定的函数
¥Returns: <Function> The bound function
返回的函数将是提供的回调函数的封装器。当调用返回的函数时,抛出的任何错误都会被路由到域的 'error'
事件。
¥The returned function will be a wrapper around the supplied callback
function. When the returned function is called, any errors that are
thrown will be routed to the domain's 'error'
event.
const d = domain.create();
function readSomeFile(filename, cb) {
fs.readFile(filename, 'utf8', d.bind((er, data) => {
// If this throws, it will also be passed to the domain.
return cb(er, data ? JSON.parse(data) : null);
}));
}
d.on('error', (er) => {
// An error occurred somewhere. If we throw it now, it will crash the program
// with the normal line number and stack message.
});
domain.enter()
#
enter()
方法是 run()
、bind()
和 intercept()
方法用来设置活动域的管道。它将 domain.active
和 process.domain
设置为域,并将域隐式推送到域模块管理的域堆栈上(有关域堆栈的详细信息,请参见 domain.exit()
)。对 enter()
的调用界定了一系列异步调用和绑定到域的 I/O 操作的开始。
¥The enter()
method is plumbing used by the run()
, bind()
, and
intercept()
methods to set the active domain. It sets domain.active
and
process.domain
to the domain, and implicitly pushes the domain onto the domain
stack managed by the domain module (see domain.exit()
for details on the
domain stack). The call to enter()
delimits the beginning of a chain of
asynchronous calls and I/O operations bound to a domain.
调用 enter()
只改变活动域,不改变域本身。enter()
和 exit()
可以在单个域上调用任意次数。
¥Calling enter()
changes only the active domain, and does not alter the domain
itself. enter()
and exit()
can be called an arbitrary number of times on a
single domain.
domain.exit()
#
exit()
方法退出当前域,将其从域堆栈中弹出。任何时候执行将切换到不同异步调用链的上下文,确保退出当前域很重要。对 exit()
的调用界定了异步调用链和绑定到域的 I/O 操作链的结束或中断。
¥The exit()
method exits the current domain, popping it off the domain stack.
Any time execution is going to switch to the context of a different chain of
asynchronous calls, it's important to ensure that the current domain is exited.
The call to exit()
delimits either the end of or an interruption to the chain
of asynchronous calls and I/O operations bound to a domain.
如果有多个嵌套域绑定到当前执行上下文,则 exit()
将退出任何嵌套在该域中的域。
¥If there are multiple, nested domains bound to the current execution context,
exit()
will exit any domains nested within this domain.
调用 exit()
只改变活动域,不改变域本身。enter()
和 exit()
可以在单个域上调用任意次数。
¥Calling exit()
changes only the active domain, and does not alter the domain
itself. enter()
and exit()
can be called an arbitrary number of times on a
single domain.
domain.intercept(callback)
#
-
callback
<Function> 回调函数¥
callback
<Function> The callback function -
返回:<Function> 截获的函数
¥Returns: <Function> The intercepted function
此方法和 domain.bind(callback)
差不多。但是,除了捕获抛出的错误外,它还会拦截作为第一个参数发送给函数的 Error
对象。
¥This method is almost identical to domain.bind(callback)
. However, in
addition to catching thrown errors, it will also intercept Error
objects sent as the first argument to the function.
这样,常见的 if (err) return callback(err);
模式可以在一个地方用单个错误句柄替换。
¥In this way, the common if (err) return callback(err);
pattern can be replaced
with a single error handler in a single place.
const d = domain.create();
function readSomeFile(filename, cb) {
fs.readFile(filename, 'utf8', d.intercept((data) => {
// Note, the first argument is never passed to the
// callback since it is assumed to be the 'Error' argument
// and thus intercepted by the domain.
// If this throws, it will also be passed to the domain
// so the error-handling logic can be moved to the 'error'
// event on the domain instead of being repeated throughout
// the program.
return cb(null, JSON.parse(data));
}));
}
d.on('error', (er) => {
// An error occurred somewhere. If we throw it now, it will crash the program
// with the normal line number and stack message.
});
domain.remove(emitter)
#
-
emitter
<EventEmitter> | <Timer> 要从域中删除的触发器或定时器¥
emitter
<EventEmitter> | <Timer> emitter or timer to be removed from the domain
domain.add(emitter)
的反义词。从指定的触发器中删除域处理。
¥The opposite of domain.add(emitter)
. Removes domain handling from the
specified emitter.
domain.run(fn[, ...args])
#
-
fn
<Function> -
...args
<any>
在域上下文中运行提供的函数,隐式绑定在该上下文中创建的所有事件触发器、定时器和底层请求。可选地,参数可以传给函数。
¥Run the supplied function in the context of the domain, implicitly binding all event emitters, timers, and low-level requests that are created in that context. Optionally, arguments can be passed to the function.
这是使用域的最基本方式。
¥This is the most basic way to use a domain.
const domain = require('node:domain');
const fs = require('node:fs');
const d = domain.create();
d.on('error', (er) => {
console.error('Caught error!', er);
});
d.run(() => {
process.nextTick(() => {
setTimeout(() => { // Simulating some various async stuff
fs.open('non-existent file', 'r', (er, fd) => {
if (er) throw er;
// proceed...
});
}, 100);
});
});
在本例中,将触发 d.on('error')
句柄,而不是使程序崩溃。
¥In this example, the d.on('error')
handler will be triggered, rather
than crashing the program.
域和 promise#
¥Domains and promises
从 Node.js 8.0.0 开始,promise 的句柄在调用 .then()
或 .catch()
本身的域内运行:
¥As of Node.js 8.0.0, the handlers of promises are run inside the domain in
which the call to .then()
or .catch()
itself was made:
const d1 = domain.create();
const d2 = domain.create();
let p;
d1.run(() => {
p = Promise.resolve(42);
});
d2.run(() => {
p.then((v) => {
// running in d2
});
});
可以使用 domain.bind(callback)
将回调绑定到特定域:
¥A callback may be bound to a specific domain using domain.bind(callback)
:
const d1 = domain.create();
const d2 = domain.create();
let p;
d1.run(() => {
p = Promise.resolve(42);
});
d2.run(() => {
p.then(p.domain.bind((v) => {
// running in d1
}));
});
域不会干扰 promise 的错误处理机制。换句话说,对于未处理的 Promise
拒绝,不会触发 'error'
事件。
¥Domains will not interfere with the error handling mechanisms for
promises. In other words, no 'error'
event will be emitted for unhandled
Promise
rejections.