领域模块可用性问题剖析隐式行为开发者可以创建新的领域,然后简单地运行domain.enter(),然后,它充当无法观察的未来投掷者any异常的通用捕获器允许模块作者拦截不同模块中不相关代码的异常,防止代码的始作俑者知道自己的异常。这是一个间接链接的模块如何影响另一个模块的示例://modulea.jsconstb=require('./b');constc=require('./c');//moduleb.jsconstd=require('domain').create();d.on('error',()=>{/*silenceeverything*/});d.enter();//模块c.jsconstdep=require('some-dep');dep.method();//哦哦!这种方法实际上并不存在。由于模块b进入域但从未退出,任何未捕获的异常都将被吞噬,不让模块c知道为什么它没有运行整个脚本,留下可能部分填充的module.exports。这样做不同于侦听“uncaughtException”,因为后者明确意味着全局捕获错误,另一个问题是域在任何“uncaughtException”处理程序之前被处理,并阻止它们运行。另一个问题是,如果事件发射器上没有设置“错误”处理程序,域会自动路由错误,对此没有可选的插入机制,而是自动传播到整个异步链。这可能看起来很有用,但是一旦异步调用是两个或多个模块深度,其中一个不包含错误处理程序,域的创建者会突然捕获意外异常,而抛出者的异常将被作者忽略.下面是一个简单的示例,说明缺少“错误”处理程序如何允许活动域拦截错误:constdomain=require('domain');constnet=require('net');constd=domain.create();d.on('error',(err)=>console.error(err.message));d.run(()=>net.createServer((c)=>{c.end();c.write('bye');}).listen(8000));即使通过d.remove(c)手动删除连接也不会阻止连接的错误被自动拦截。困扰错误路由和异常处理的一个失败是错误冒泡的不一致,下面是一个示例,说明嵌套域如何不根据异常发生的时间来冒泡异常:constdomain=require('domain');constnet=require('net');constd=domain.create();d.on('error',()=>console.error('d截获错误'));d.run(()=>{constserver=net.createServer((c)=>{conste=domain.create();//没有设置“错误”处理程序。e.run(()=>{//这不会是被d的错误处理程序捕获。setImmediate(()=>{thrownewError('thrownfromsetImmediate');});//虽然这个会冒泡到d的错误处理程序。thrownewError('immediatelythrown');});}).听(8080);});可以预期嵌套域始终保持嵌套状态,并始终将异常传播到域堆栈中,或者异常永远不会自行冒泡,不幸的是,这两种情况都可能发生,从而导致潜在的混乱行为,甚至可能难以调试时序违规。API差距虽然基于使用EventEmitter的API可以使用bind()并且errback样式的回调可以使用intercept(),但隐式绑定到活动域的替代API必须在run()内部执行。这意味着如果模块作者想要使用上述机制的替代方案来支持域,他们必须自己手动实现域支持,而不是能够利用现有的隐式机制。错误传播如果可能的话,跨嵌套域传播错误并不简单,现有文档显示了一个简单示例,说明如果请求处理程序中存在错误,如何关闭()http服务器,但它没有解释的是如果请求处理程序isAnotherasynchronousrequesttocreateanotherdomaininstance,如何关闭服务器,用下面一个错误传播失败的简单例子:constd1=domain.create();d1.foo=true;//自定义成员以在consoled1中更加可见。on('error',(er)=>{/*处理错误*/});d1.run(()=>setTimeout(()=>{constd2=domain.create();d2.bar=43;d2.on('error',(er)=>console.error(er.message,domain._stack));d2.run(()=>{setTimeout(()=>{setTimeout(()=>{thrownewError('outer');});thrownewError('inner');});});}));即使域实例用于本地存储,仍然可以访问资源。没有办法让错误继续从d2传播回d1。快速检查可能会告诉我们,简单地从d2的域“错误”处理程序中抛出将允许d1然后捕获异常并执行它自己的错误处理程序,尽管事实并非如此,在检查域之后。_stack你会看到堆栈只包含d2.这可能被认为是API的失败,但即使它确实以这种方式运行,仍然存在传递异??步执行中的分支失败的事实的问题,并且该分支中的所有进一步操作都必须停止。在http请求处理程序的示例中,如果我们触发多个异步请求,每个请求都将write()的数据发送回客户端,尝试将write()发送到已关闭的句柄将产生更多错误,异常资源清理。以下脚本包含一个更复杂的示例,如果在给定连接或其任何依赖项中发生异常,则在小型资源依赖项树中进行适当清理,将脚本分解为基本操作:'usestrict';constdomain=require('domain');constEE=require('events');constfs=require('fs');constnet=require('net');constutil=require('util');constprint=process._rawDebug;constpipeList=[];constFILENAME='/tmp/tmp.tmp';constPIPENAME='/tmp/node-domain-example-';constFILESIZE=1024;letuid=0;//设置临时资源constbuf=Buffer.alloc(FILESIZE);for(leti=0;i
