Node.js 为什么节点更喜欢错误优先回调?

Node.js 为什么节点更喜欢错误优先回调?,node.js,callback,Node.js,Callback,节点程序员通常使用如下范例: let callback = function(err, data) { if (err) { /* do something if there was an error */ } /* other logic here */ }; var errc = require('errc'); var fs = require('fs'); var isError = function(obj) { try { return obj instanceof

节点程序员通常使用如下范例:

let callback = function(err, data) {
  if (err) { /* do something if there was an error */ }

  /* other logic here */
};
var errc = require('errc');
var fs = require('fs');

var isError = function(obj) {
  try { return obj instanceof Error; } catch(e) {}
  return false;
};

var callback = function(data) {
  if (isError(data)) {
    console.log('Error:', data.message);
  } else {
    console.log('Success:', data);
  }
};

fs.readFile('example.txt', errc(callback));
为什么不将函数简化为只接受一个参数,即错误或响应

let callback = function(data) {
  if (isError(data)) { /* do something if there was an error */ }

  /* other logic here */  
};
似乎更简单。我能看到的唯一缺点是函数不能将错误作为实际的预期返回值返回——但我相信这是一个极其微不足道的用例

为什么错误优先模式被视为标准模式

编辑:iError的实现

let isError = obj => obj != null && obj instanceof Error;

另一个编辑:我的替代方法是否可能比节点约定更方便,因为只接受一个参数的回调更有可能被非回调用例重用?

因为没有这个约定,开发人员将不得不维护不同的签名和API,不知道将错误放在arguments数组的何处

在大多数情况下,可能有许多参数,但只有一个错误-并且您知道在哪里可以找到它

Joyent甚至在他们参与更多的时候:

回调是传递事件的最基本方式 异步的。用户向您传递一个函数(回调),然后 稍后异步操作完成时调用它。 通常的模式是调用回调作为回调(err, 结果),其中err和result中只有一个非空,具体取决于 操作是否成功

(请参阅下面的“更新”,了解npm模块如何使用问题中的回调约定。)

这只是一个惯例。Node也可以使用您建议的约定,但您注意到在成功时不能将错误对象作为预期值返回,这可能是问题,也可能不是问题,具体取决于您的特定需求

当前节点约定的问题是,有时回调可能不需要任何数据,
err
是它们所采用的唯一参数,有时函数在成功时需要多个值—例如,请参阅

request(url, (err, res, data) => {
  if (err) {
    // you have error
  } else {
    // you have both res and data
  }
});
有关上述代码的完整示例,请参见

但是,即使在使用多个参数的函数中,您也可以将第一个参数设置为错误,即使这样,我也看不出您的样式有任何问题

错误优先节点样式回调最初由Ryan Dahl使用,现在它非常通用,适用于任何接受回调的异步函数。并不是说这个约定比你建议的好或者更糟,而是有了一个约定-不管它是什么-使得回调和回调函数的组合成为可能,像这样的模块依赖于此

事实上,我看到您的想法优于经典节点约定的一种方式——不可能在同时定义了error和第一个non-error参数的情况下调用回调,这对于节点样式的回调是可能的,有时也会发生。不过,这两种约定都可能会调用回调两次,这是一个问题

但是JavaScript中还有另一个广泛使用的约定,尤其是Node,如果无法同时定义错误和数据,并且无法调用回调两次,则返回承诺,而不是像节点样式回调或样式回调那样在
if
中显式检查错误值,您可以分别添加仅获取相关数据的成功和失败回调

所有这些样式在它们的功能上几乎相同:

nodeStyle(params, function (err, data) {
  if (err) {
    // error
  } else {
    // success
  }
};

yourStyle(params, function (data) {
  if (isError(data)) {
    // error
  } else {
    // success
  }
};

promiseStyle(params)
  .then(function (data) {
    // success
  })
  .catch(function (err) {
    // error
  });
承诺可能更方便满足您的需求,而且这些承诺已经得到了很多工具的广泛支持,例如和其他工具

您可以看到一些其他答案,其中我解释了回调和承诺之间的区别,以及如何更详细地一起使用,这可能对您在这种情况下有所帮助:

当然,我看不出有什么理由不能编写一个模块,将节点样式的回调转换为您的样式的回调,反之亦然,承诺也是如此,非常类似于Bluebird。如果使用回调风格对您来说更方便,那么这显然是可行的

更新 我刚刚在npm上发布了一个模块,您可以使用它来获得首选的回调样式:

您可以通过以下方式安装它并在项目中使用:

npm install errc --save
它允许您拥有如下代码:

let callback = function(err, data) {
  if (err) { /* do something if there was an error */ }

  /* other logic here */
};
var errc = require('errc');
var fs = require('fs');

var isError = function(obj) {
  try { return obj instanceof Error; } catch(e) {}
  return false;
};

var callback = function(data) {
  if (isError(data)) {
    console.log('Error:', data.message);
  } else {
    console.log('Success:', data);
  }
};

fs.readFile('example.txt', errc(callback));
有关更多示例,请参见:

我写这个模块是为了举例说明如何操作函数和回调以满足您的需求,但是我在MIT许可下发布了它,并在npm上发布了它,以便您可以在实际项目中使用它


这展示了Node及其回调模型的灵活性,以及编写高阶函数以创建适合您需要的API的可能性。我发布它的目的是希望它可以作为理解节点回调样式的一个示例。

您打算如何可靠地检查
iError
中的错误?编辑为包含
iError
实现!谢谢aring,尽管我完全理解错误优先回调的用法!我只是对这样一种格式是如何成为广为接受的惯例感兴趣。@GershomMaes,回答关于你的方法的问题:在我看来,你的方法至少慢了一些。很好的回答,很多有用的附加信息!