节点程序员通常使用这样的范例:
var callback = function(err, data) {
if (err) { /* do something if there was an error */ }
/* other logic here */
};
为什么不简化函数只接受一个参数,即错误或响应?
var callback = function(data) {
if (isError(data)) { /* do something if there was an error */ }
/* other logic here */
};
似乎更简单。我能看到的唯一缺点是函数不能将错误作为实际预期的返回值返回 - 但我相信这是一个非常微不足道的用例。
为什么错误优先模式被认为是标准模式?
编辑:isError
的实施:
var isError = function(obj) {
try { return obj instanceof Error; } catch(e) {}
return false;
};
另一个编辑:我的备用方法是否有可能比节点约定更方便,因为只接受一个参数的回调更可能是非回调用例的可重用?
答案 0 :(得分:9)
(请参阅下面的“更新”,了解npm模块使用问题中的回调约定。)
这只是一个惯例。 Node也可以使用你建议的约定 - 除了你注意到你不能将错误对象作为成功的预期值返回,这可能是也可能不是问题,这取决于你的特定要求
当前节点约定的事情是,有时回调可能不会期望任何数据,err
是他们唯一的参数,有时函数会在成功时期望多个值 - 例如,请参阅
request(url, (err, res, data) => {
if (err) {
// you have error
} else {
// you have both res and data
}
});
有关上述代码的完整示例,请参阅this answer。
但即使在带有多个参数的函数中,您也可以将第一个参数设置为错误,即使这样,我也没有看到您的样式有任何问题。
错误优先的节点式回调是Ryan Dahl最初使用的,它现在非常通用,并且可用于任何采用回调的异步函数。并不是说这个约定比你的建议更好或者更糟,但是有约定 - 无论它是什么 - 使得回调和回调的组合成为可能,而像async这样的模块依赖于这一点。
实际上,我看到了一种方法,你的想法优于传统的Node约定 - 不可能用错误和定义的第一个非错误参数调用回调,这对于Node样式的回调是有可能的,有时可以发生。这两个约定都可能有两次调用回调 - 这是一个问题。
但是在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
});
Promise可能更方便您的需求,并且已经广泛支持使用许多工具,例如Bluebird和其他人。
您可以看到其他一些答案,其中我解释了回调和承诺之间的区别以及如何更详细地使用它们,这可能对您有所帮助:
当然,我认为你没有理由不编写一个将Node风格的回调转换为你的样式回调的模块,反之亦然,而且承诺也是如此,就像promisify和asCallback一样在蓝鸟工作。如果使用你的回调样式对你来说更方便的话,这似乎是可行的。
我刚刚在npm上发布了一个模块,您可以使用它来获得您首选的回调样式:
您可以安装它并在项目中使用:
npm install errc --save
它允许您拥有这样的代码:
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的可能性。我发布它是希望它可以作为一个例子来理解Node回调样式。
答案 1 :(得分:4)
因为没有这个约定,开发人员必须维护不同的签名和API,而不知道将错误放在arguments数组中的哪个位置。
在大多数情况下,可能有很多参数,但只有一个错误 - ,你知道在哪里找到它。
Joyent甚至在他们参与的时候wrote about this:
回调是提供活动的最基本方式 异步。用户通过了一个函数(回调)和你 异步操作完成后稍后调用它。 通常的模式是回调被调用为回调(错误, 结果),其中只有一个错误和结果是非空的,取决于 操作是成功还是失败。