为什么我需要2个参数来回调?
why do i need 2 parameters for callback?
我在读取 firefox-json-书签的节点脚本中有一个简单的异步 reader。
当我删除回调函数中的第一个参数 (err) 时,出现错误。
是什么原因?为什么 err 与 e 不同?
app.js
var fs = require('fs'), obj;
fs.readFile(__dirname + '/bookmarks.json', 'utf8', handleFile);
function handleFile( err, data ) { // why is the first parameter needed?
try {
obj = JSON.parse( JSON.stringify(data) );
console.log(obj);
} catch (e) {
console.log(e);
//console.log(err);
}
}
每次您调用一个函数时,该函数都会被推送到称为调用堆栈的函数堆栈中。当该函数 return 是一个值时,它会从堆栈中弹出。调用堆栈描述了您在程序中的位置以及如何到达那里。
同步代码
想象一下整个程序过程中的调用堆栈。
function a() { return b() }
function b() { return c() }
function c() { throw new Error() }
a();
首先,a
被调用,所以我们将它添加到堆栈中。
[ a ]
然后 a
调用 b
,因此我们也将其添加到堆栈中。
[ a, b ]
然后 b
调用 c
.
[ a, b, c ]
然后 c
抛出一个错误。此时,调试器可以告诉您 c
是抛出错误的函数,您最终到达 c
,先是 a
,然后是 b
。这适用于常规同步代码,例如 JSON.parse
.
异步代码
异步代码在函数 return 编辑后继续 运行。例如:
function a() {
setTimeout(function() { console.log(2); }, 10000);
return 1;
}
如果你调用a
,那么它会被压入调用栈,然后return 1
被弹出调用栈。大约 10 秒后 2
将打印到控制台中。
如果超时改为这样做会发生什么?
function a() {
setTimeout(function() { throw new Error(); }, 10000);
return 1;
}
将抛出错误,但调用堆栈将为空。可以想象,这对开发人员来说不是很有用。
如果我们想要异步 return 一个值,这也是一个问题。当异步事件发生时(超时、read/write、网络等),函数已经 returned.
相反,我们可以使用一种称为 Continuation-Passing Style 的形式,通常称为 回调 。除了调用我们的异步函数外,我们还向它传递函数(延续),我们在它完成时将其请求给 运行。请记住,这可以在函数 return 编辑了一个值之后!
在 Node.js 中,这些回调有两个目的:
错误
如果在执行异步工作时发生错误,标准做法是调用回调并将错误作为第一个参数。你会经常看到下面的代码。
foo.doAsyncBar(function(err, baz) {
if(err) throw err;
// do something with baz
});
通过将错误传递给回调而不是抛出它,我们可以自行决定如何最好地处理它。我们可以直接扔掉,如上所示,也可以用更优雅的方式处理。
Return
希望函数不会出错,在这种情况下,一般做法是将 null
作为第一个参数传递给回调。这让编写处理代码的开发人员知道函数没有错误,并且 return 值在下一个参数中。
有关 Node.js 错误处理的更深入的文章,请参阅 Joyent 的 Production Practices document for Errors。
我在读取 firefox-json-书签的节点脚本中有一个简单的异步 reader。 当我删除回调函数中的第一个参数 (err) 时,出现错误。 是什么原因?为什么 err 与 e 不同?
app.js
var fs = require('fs'), obj;
fs.readFile(__dirname + '/bookmarks.json', 'utf8', handleFile);
function handleFile( err, data ) { // why is the first parameter needed?
try {
obj = JSON.parse( JSON.stringify(data) );
console.log(obj);
} catch (e) {
console.log(e);
//console.log(err);
}
}
每次您调用一个函数时,该函数都会被推送到称为调用堆栈的函数堆栈中。当该函数 return 是一个值时,它会从堆栈中弹出。调用堆栈描述了您在程序中的位置以及如何到达那里。
同步代码
想象一下整个程序过程中的调用堆栈。
function a() { return b() }
function b() { return c() }
function c() { throw new Error() }
a();
首先,a
被调用,所以我们将它添加到堆栈中。
[ a ]
然后 a
调用 b
,因此我们也将其添加到堆栈中。
[ a, b ]
然后 b
调用 c
.
[ a, b, c ]
然后 c
抛出一个错误。此时,调试器可以告诉您 c
是抛出错误的函数,您最终到达 c
,先是 a
,然后是 b
。这适用于常规同步代码,例如 JSON.parse
.
异步代码
异步代码在函数 return 编辑后继续 运行。例如:
function a() {
setTimeout(function() { console.log(2); }, 10000);
return 1;
}
如果你调用a
,那么它会被压入调用栈,然后return 1
被弹出调用栈。大约 10 秒后 2
将打印到控制台中。
如果超时改为这样做会发生什么?
function a() {
setTimeout(function() { throw new Error(); }, 10000);
return 1;
}
将抛出错误,但调用堆栈将为空。可以想象,这对开发人员来说不是很有用。
如果我们想要异步 return 一个值,这也是一个问题。当异步事件发生时(超时、read/write、网络等),函数已经 returned.
相反,我们可以使用一种称为 Continuation-Passing Style 的形式,通常称为 回调 。除了调用我们的异步函数外,我们还向它传递函数(延续),我们在它完成时将其请求给 运行。请记住,这可以在函数 return 编辑了一个值之后!
在 Node.js 中,这些回调有两个目的:
错误
如果在执行异步工作时发生错误,标准做法是调用回调并将错误作为第一个参数。你会经常看到下面的代码。
foo.doAsyncBar(function(err, baz) {
if(err) throw err;
// do something with baz
});
通过将错误传递给回调而不是抛出它,我们可以自行决定如何最好地处理它。我们可以直接扔掉,如上所示,也可以用更优雅的方式处理。
Return
希望函数不会出错,在这种情况下,一般做法是将 null
作为第一个参数传递给回调。这让编写处理代码的开发人员知道函数没有错误,并且 return 值在下一个参数中。
有关 Node.js 错误处理的更深入的文章,请参阅 Joyent 的 Production Practices document for Errors。