try { process.nextTick(function() { throw new Error('Catch Me If You Can'); }); } catch (e) { console.log('Exception caught:', e); }
/** * Block . */ function Block(errback) { this._parent=Block.current; this._errback=errback; } Block.current=null; /** * , , , * guard(). * , . * * Example: stream.on('end', Block.guard(function() { ... })); */ Block.guard=function(f) { if (this.current) return this.current.guard(f); else return f; }; /** * . - (" try"). * - ('catch'). */ Block.begin=function(block, rescue) { var ec=new Block(rescue); return ec.trap(block); }; /** * function(err), , * ( , ). * err true. * * Example: request.on('error', Block.errorHandler()) */ Block.errorHandler=function() { // Capture the now current Block for later var current=this.current; return function(err) { if (!err) return; if (current) return current.raise(err); else throw err; }; }; /** * . error handler, . * raise(...) . , * throw. * , error handler' , * error handler'. */ Block.prototype.raise=function(err) { if (this._errback) { try { this._errback(err); } catch (nestedE) { if (this._parent) this._parent.raise(nestedE); else throw nestedE; } } else { if (this._parent) this._parent.raise(err); else throw(err); } }; /** * callback . * raise() . * return value undefined . */ Block.prototype.trap=function(callback) { var origCurrent=Block.current; Block.current=this; try { var ret=callback(); Block.current=origCurrent; return ret; } catch (e) { Block.current=origCurrent; this.raise(e); } }; /** * , . * trap(), . */ Block.prototype.guard=function(f) { if (f.__guarded__) return f; var self=this; var wrapped=function() { var origCurrent=Block.current; Block.current=self; try { var ret=f.apply(this, arguments); Block.current=origCurrent; return ret; } catch (e) { Block.current=origCurrent; self.raise(e); } }; wrapped.__guarded__=true; return wrapped; };
Block.begin(function() { process.nextTick(Block.guard(function() { throw new Error; })); }, function(err) { console.log('Exception caught:', err); });
Now the exception is processed, and we can not drop the server. This also works with setTimeout, EventEmitter, callbacks for requests to the database, and anything else.function(err) { ; next(err); }
function(err) { ; next(err); }
function(err) { ; next(err); }
. You can also use inline functions in Block.begin calls for greater visual similarity to try / catch, but I prefer to use named callbacks to increase readability. function handleUserAgent(req, res, next) { return Block.begin(process, next); function process() { jsonifyRequest(req, withRequest); // , jsonifyRequest , // Block.guard() } function withRequest(requestObj) { var r=validators.UserAgentRecord(requestObj, {fix:true}); if (!r.valid) { res.writeHead(400); return res.end('Invalid request object: ' + r.reason); } var uar=r.object; if (uar.token) { // Verify //return handler.verifyUserAgent(uar); throw new Error('verifyUserAgent not yet implemented'); } else { // Create uar.token=null; uar.type='auth'; // TODO: Maybe support unauth in the future? handler.createUserAgent(uar, Block.guard(withUserAgent)); } } function withUserAgent(userAgent) { var r=validators.UserAgentRecord(userAgent, {fix:true}); return respondJson(r.object, res); } }
request: function(options, callback) { var req=http.request(options, function(res) { var text=''; res.setEncoding('utf8'); res.on('data', function(chunk) { text+=chunk; }); res.on('end', Block.guard(function() { callback(new CouchResponse(res, text)); })); res.on('error', Block.errorHandler()); }); req.on('error', Block.errorHandler()); req.end(); }
try{try{}catch{}}catch{}
). This is useful in code for frameworks that should do some work within a block created by someone else's code.Source: https://habr.com/ru/post/147233/
All Articles