回调函数究竟是如何在nodejs中运行的?

时间:2016-09-09 06:09:51

标签: node.js callback

我经历了很多带有回调的nodejs的例子,但是不明白它们究竟是如何工作的。我知道它们是在函数之后执行的,它们已经完成了它的一部分,但是我并不把回调理解为函数。让我举个例子:

function processData (callback) {
  fetchData(function (err, data) {
    if (err) {
      console.log("An error has occured. Abort everything!");
      callback(err);
    }
    data += 1;
    callback(data);
  });
}

在这里,回调(错误)回调(数据)如何执行,如何,它是我们传递给的其他一些函数的别名参数 - 错误/数据?如果是这样,当我们编写回调(参数)时它会调用哪个函数?

这是另一个例子:

var express =   require("express");
var bodyParser =    require("body-parser");
var multer  =   require('multer');
var app =   express();
app.use(bodyParser.json());
var storage =   multer.diskStorage({
  destination: function (req, file, callback) {
    callback(null, './uploads');
  },
  filename: function (req, file, callback) {
    callback(null, file.fieldname + '-' + Date.now());
  }
});
var upload = multer({ storage : storage }).array('userPhoto',2);

app.get('/',function(req,res){
      res.sendFile(__dirname + "/index.html");
});

app.post('/api/photo',function(req,res){
    upload(req,res,function(err) {
        //console.log(req.body);
        //console.log(req.files);
        if(err) {
            return res.end("Error uploading file.");
        }
        res.end("File is uploaded");
    });
});

app.listen(3000,function(){
    console.log("Working on port 3000");
});

我再次理解其他类型的回调 - 假设,这里是fs.readdir

fs.readdir(path, function (err, files) {
    if (err)  console.error(err);
    for (var i = 0; i<files.length; i++)  console.log(files[i];
}
console.log("done");

我知道这是如何执行的,最有可能首先打印完成,然后在使用包含文件列表的文件执行readdir后立即打印文件列表。

但我并不完全理解第一个和第二个代码段。有人可以用简单的术语解释一下,特定于multer代码片段吗?

2 个答案:

答案 0 :(得分:2)

回调和异步行为是两个不同但相关的事情。它们之间的关系是异步代码可以使用回调来按预期的顺序执行代码。但回调本身只是语言将函数视为对象的结果。

在您的示例中,您可以看到回调故事的两个方面。一个是实现,另一个是用法。第一个代码是如何实现接受回调的函数的示例。最后一个是如何使用接受回调函数的示例。

为简单起见,我们暂时忽略异步行为并实现同步回调。

以下是循环数组以构造字符串的函数示例:

function joiner (arr, callback) {
  var return_value = "";
  for (var i=0; i<arr.length; i++) {
    return_value += callback(arr[i]); // use callback on each item
  }
  return return_value;
}

一旦我们实施了上述功能,我们就可以使用它:

var result = joiner(['this','is','cool'],function(x) {
    return "--> " + x + "!";
});

那么,我们如何知道回调接受哪些参数?它接受调用者传递给它的内容。

我们如何知道传递给回调函数的参数?我们传递了函数定义接受的内容?

如果这听起来很圆,那就是。那么我们如何真正知道传递给回调的参数是:

  • 如果它是我们创建的API,我们可以做任何我们想要的事情
  • 如果它是其他人创建的API,请阅读文档(或者如果您的IDE可以执行此操作,请查看intellisense)

回调变为元

上面的解释当然只是最简单的回调示例:接受回调的函数。快速示例(实际上是您的第一个示例)展示了如何让您变得更加复杂。您可以编写接受回调的函数来接受回调。

接受回调接受回调的函数的简化示例如下所示:

function processData (data) {
    return "<" + data + ">";
}

function render (callback) {
    return callback(processData);
}

因此上面的render函数接受一个回调,该回调将传递一个返回字符串的回调。所以如果我们现在这样做:

var result = render(function(callback){
    return "--" + callback('hello');
});

我们会得到:

--<hello>

正如您所看到的,我们传递给render的回调不知道如何处理数据,但render做了(它调用processData)。并且render不知道数据是什么,但我们传递给它的回调('hello')。能够将回调传递给回调允许两段代码合作而不会暴露彼此的实现细节。

异步代码使用此技术来排序正在执行的代码序列。因为异步代码不能返回一个值,所以你只能传递一个回调函数,这样一旦它获得了它的值,你的代码(回调)就可以继续了。

如果您考虑一下,可以轻松实现Express API,以便请求处理程序回调必须返回HTML页面而不是调用res.send()。实际上,这就是框架在许多语言中的工作方式。但这意味着请求处理程序无法执行异步代码来获取数据。因为Express并不希望限制您仅使用同步代码,所以它会使用回调方法传递对象(res)。这种&#34;设计模式&#34;被称为monad。你在Express中看到的是一个延续monad,而你在fs.readdir()中看到的是一个I / O monad。

答案 1 :(得分:-1)

首先,您感到困惑,因为您没有遵循Node JS标准将参数传递给异步函数。每个异步回调必须采用2个参数。首先应始终为div3,第二应始终为error。请参阅如何定义datareaddir的回调。

所以你的代码应该看起来像

fetchData

您将在调用它时为processData定义回调函数,如

function processData (callback) {
  fetchData(function (err, data) {
    if (err) {
      console.log("An error has occured. Abort everything!");
      callback(err, null); // here data will be null
    }
    data += 1;
    callback(null, data); // here err will be null.
  });
}

这样,在回调中,您可以确定该功能是成功还是失败。