为什么 Express 的默认错误处理程序在从可读流发出时捕获错误?

Why does the Express' default error handler catch error when emitting from Readable stream?

我使用命令行 express stream_test 安装了一个样板 Express 应用程序。我将默认的 /routes/index.js 替换为:

var express = require('express');
var router = express.Router();
var ReadFile = require('./readFile.js');

/* GET home page. */
router.get('/', function(req, res, next) {
  var readFile = ReadFile();
  readFile
  .on('data', function(data) {
    res.write(data);
  })
  .on('end', function() {
    res.end();
  })
  .on('error', function(err) {
    console.log(err);
  });
});

module.exports = router;

readFile.js 只是 fs.createReadStream:

的简单包装
var Readable = require('stream').Readable;
var util = require('util');
var fs = require('fs');

function ReadFile(options) {
  if (!(this instanceof ReadFile)) {
    return new ReadFile(options);
  }
  Readable.call(this);

  options = options || {};

  var self = this;

  fs.createReadStream('pride_and_prejudice.txt')
  .on('data', function(data) {
    self.push(data);
  })
  .on('end', function(end) {
    self.push(null);
  });

}

util.inherits(ReadFile, Readable);
ReadFile.prototype._read = function _readGetDeals() {};

module.exports = ReadFile;

完全没问题。调用路由时将pride_and_prejudice.txt的内容输出到屏幕。

但是假设不满足某些要求,我想在流式传输数据之前抛出一个错误:

var Readable = require('stream').Readable;
var util = require('util');
var fs = require('fs');

function ReadFile(options) {
  if (!(this instanceof ReadFile)) {
    return new ReadFile(options);
  }
  Readable.call(this);

  options = options || {};

  var self = this;

  if (!options.okay) {
    return self.emit('error', new Error('Forced crash'));
  }

  fs.createReadStream('pride_and_prejudice.txt')
  .on('data', function(data) {
    self.push(data);
  })
  .on('end', function(end) {
    self.push(null);
  });

}

options.okayfalse 时会引发错误 Forced crash.。我希望在监听 error 时捕获 index.js 路由中的错误。但是处理程序永远不会执行。令我惊讶的是 app.js 中的默认错误处理程序正在捕获错误:

if (app.get('env') === 'development') {
  app.use(function(err, req, res, next) {
    res.status(err.status || 500);
    res.render('error', {
      message: err.message,
      error: err
    });
  });
}

这让我抓狂。错误是如何结束的?为什么事件监听器没有捕捉到它?

1) 当你发出 error ReadFile returns 什么都没有,以及最近的路由器模块之外的错误陷阱时,然后在调用 ReadFile 之后她调用了。

2) 您可以使用 try-catch:

try {
  var readFile = ReadFile();
} catch(e) {
  console.log(e);
}

3) 或使用错误回调:

function ReadFile(options, errorCallback) {
  /**...**/
  if (!options.okay) {
    errorCallback( new Error('Forced crash') );
    return;
  }
  /**...**/ 
}

我的想法是 'error' 事件甚至在流对象完成创建之前就已发出。结果节点找不到您附加的 'error' 侦听器,因为它尚未创建并将其作为 Unhandled 'error' event.

抛出

一种解决方案是使用 setImmediate 延迟事件发出。然后它将创建流对象并在发出错误之前首先绑定错误侦听器:

 if (!options.okay) {
    setImmediate(function(){ 
      self.emit('error', new Error('Forced crash')); 
    });
    return;
  }