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

Ger*_*ica 13 callback node.js

节点程序员通常使用这样的范例:

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

    /* other logic here */

};
Run Code Online (Sandbox Code Playgroud)

为什么不简化函数只接受一个参数,即错误或响应?

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

    /* other logic here */

};
Run Code Online (Sandbox Code Playgroud)

似乎更简单.我能看到的唯一缺点是函数不能将错误作为实际预期的返回值返回 - 但我相信这是一个非常微不足道的用例.

为什么错误优先模式被认为是标准的?

编辑:执行isError:

var isError = function(obj) {
    try { return obj instanceof Error; } catch(e) {}
    return false;
};
Run Code Online (Sandbox Code Playgroud)

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

rsp*_*rsp 14

(请参阅下面的"更新",了解npm模块使用问题中的回调约定.)

这只是一个惯例.Node也可以使用你建议的约定 - 除了你注意到你不能将错误对象作为成功的预期值返回,这可能是也可能不是问题,这取决于你的特定要求.

具有当前Node约定的事情是,有时回调可能不期望任何数据,并且它们err是唯一的参数,并且有时函数期望成功时有多个值 - 例如,请参阅

request(url, (err, res, data) => {
  if (err) {
    // you have error
  } else {
    // you have both res and data
  }
});
Run Code Online (Sandbox Code Playgroud)

有关上述代码的完整示例,请参阅此答案.

但是你甚至可以将第一个参数设置为错误,即使在带有多个参数的函数中也是如此,即使这样,我也没有看到你的风格有任何问题.

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

实际上,我看到了一种方法,你的想法优于传统的Node约定 - 不可能用错误和定义的第一个非错误参数调用回调,这对于Node样式的回调是可能的,有时可能发生.两种约定都可能有两次调用回调 - 这是一个问题.

但是在JavaScript中还有另一个广泛使用的约定,特别是Node,它不可能同时定义错误和数据,另外不可能两次调用回调 - 而不是回调你返回一个promise而不是显式检查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
  });
Run Code Online (Sandbox Code Playgroud)

Promise可能更方便您的需求,并且已经广泛支持使用它们的许多工具,如Bluebird和其他.

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

当然,我认为没有理由你不能编写一个模块将Node风格的回调转换为你的样式回调,反之亦然,并且与promises相同,就像promisifyasbirdall在Bluebird中工作一样.如果使用你的回调样式对你来说更方便的话,这似乎是可行的.

更新

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

您可以安装它并在项目中使用:

npm install errc --save
Run Code Online (Sandbox Code Playgroud)

它允许你有这样的代码:

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));
Run Code Online (Sandbox Code Playgroud)

有关更多示例,请参阅

我编写这个模块作为如何操作函数和回调以满足您的需求的示例,但我在MIT许可下发布它并在npm上发布,因此您可以在实际项目中使用它,如果您愿意.

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


Lif*_*ery 6

因为没有该约定,开发人员将不得不维护不同的签名和API,而又不知道将错误放置在arguments数组中的位置。

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

乔伊恩特(Joyent)甚至在参与其中时就写过这句话:

回调是异步传递事件的最基本方法。用户向您传递一个函数(回调),然后在异步操作完成后的某个时间调用它。通常的模式是将回调作为callback(err,result)调用,其中err和result中只有一个为非null,具体取决于操作是成功还是失败。