为什么我需要2个参数进行回调?

时间:2015-11-24 17:02:16

标签: json file parsing fs

我在读取firefox-json-bookmarks的节点脚本中有一个简单的异步读取器。 当我删除回调函数中的第一个参数(错误)时,我收到一个错误。 是什么原因?为什么与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);
    }
}

1 个答案:

答案 0 :(得分:1)

每次调用一个函数时,该函数都被压入一堆称为调用堆栈的函数中。当该函数返回一个值时,它将从堆栈中弹出。调用堆栈描述了您在程序中的位置以及您到达目的地的方式。

同步代码

想象一下调用堆栈贯穿整个程序的过程。

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是抛出错误的函数,并且您最终在ca然后b。这适用于常规同步代码,例如JSON.parse

异步代码

异步代码在函数返回后继续运行。例如:

function a() {
  setTimeout(function() { console.log(2); }, 10000);
  return 1;
}

如果你调用a,它将被推送到调用堆栈,然后返回1并从调用堆栈中弹出。大约10秒钟后,2将被打印到控制台中。

如果超时这样做会发生什么?

function a() {
  setTimeout(function() { throw new Error(); }, 10000);
  return 1;
}

将抛出错误,但调用堆栈将为空。可以想象,这对开发人员来说并不是很有用。

如果我们想要异步返回一个值,这也是一个问题。当异步事件发生时(超时,读/写,网络等),该函数已经返回。

相反,我们可以使用一种称为Continuation-Passing Style的形式,通常称为回调。除了调用我们的异步函数之外,我们还传递了它的函数(一个延续),我们要求它在完成后运行。请记住,这可以在函数返回值之后!

在Node.js中,这些回调有两个目的:

错误

如果在执行异步工作时发生错误,标准做法是将错误作为第一个参数调用回调。您经常会看到以下代码。

foo.doAsyncBar(function(err, baz) {
  if(err) throw err;
  // do something with baz
});

通过将错误传递给回调而不是抛出它,我们能够自己决定如何最好地处理它。我们可以抛出它,如上所示,或者我们可以更优雅地处理它。

返回

希望函数不会出错,在这种情况下,通常的做法是将null作为回调的第一个参数传递。这使得编写处理代码的开发人员知道函数没有错误,并且返回值是下一个参数之一。

有关Node.js错误处理的更深入的文章,请参阅Joyent的Production Practices document for Errors