【问题标题】:Using a callback parameter to get the value in the Anonymous Function使用回调参数获取匿名函数中的值
【发布时间】:2018-07-28 11:21:39
【问题描述】:

我遇到了以下回调函数代码。我理解代码,但我无法理解它。这似乎太违反直觉了。

function greet(name,callback1){
    callback1(name)
}
greet("John Cena", function (somerandomName) {
    console.log(somerandomName);
    return someRandomName;
}); // Output is John Cena and undefined. 

这是我从代码中了解到的:

  1. 我们定义了一个函数greet,它有两个参数namecallback1。然后我们说callback1的参数是name。 greet 函数中没有返回任何内容,为什么?

  2. 当我们调用greet 函数时,我们将第二个参数作为参数为someRandomName 的匿名函数传递。然后我们console.log(someRandomName)。 我添加了返回 someRandomName,但是这个返回不起作用,我得到了打印的语句,后面跟着 undefined

谁能用简单的话解释一下,这似乎太违反直觉了。

【问题讨论】:

  • 如果 greet() 实际上是在异步执行某些操作,那么回调工作的原因可能更容易理解。

标签: javascript asynchronous callback asynccallback


【解决方案1】:

所以我认为理解函数本身可以是参数很重要。

在本例中,您传递一个字符串作为第一个参数,然后传递一个将该字符串作为参数的函数作为第二个参数。

函数并不总是需要返回一些东西。通常,一个函数可能会对 dom 执行操作、获取数据、配置某些内容或更改预先存在的变量。当然,如果需要,您可以返回一些东西。

像你一样添加回报并没有多大作用。为了真正返回名称值,您必须像这样编写原始函数。

function greet(name,callback1){
    return callback1(name)
}

那么你可以做这样的事情

var wrestler = greet("John Cena", function (somerandomName) {
    console.log(somerandomName);
    return somerandomName;
});

console.log(wrestler) // prints John Cena

这是一个奇怪的例子,因为它没有真正的目的。这样的事情可能会帮助您了解发生了什么。

    function greet(name, callback) {
      callback(name)
    }

    greet('John Cena', function(name){
      console.log('Hello ' + name) // prints Hello John Cena
    })

   OR return something and use it to manipulate dom

   function greet(name, callback) {
      return callback(name)
   }

   var greeting = greet('John Cena', function(name){
     return 'Hello ' + name
   })

   document.getElementById('message').innerHTML = greeting

   // somewhere in HTML...
   <h1 id='message'></h1>

无论哪种方式,至少我们现在正在对第一个参数做一些事情。你可以用回调做的事情是无限的。

回调是 javascript 的一个基本特性。当函数的第一部分是异步的(例如调用 api 或数据库)时,它们会发挥很多作用。在这种情况下,第一部分将是对数据库的调用,并且在从初始调用获得值之前不会触发回调。最近,由于 Promises,这种回调模式使用较少,但回调仍然有用。

这是一个从前端到后端的通用 api 调用示例。这通常使用 Fetch Api 或 Request 或 Axios 之类的库来完成。请记住,调用端点的第一个函数需要一些时间来执行和获取数据。在返回该数据之前,回调不会触发。当然,后端会有一个函数将错误或数据发送回回调。我不想让事情变得过于复杂,而只是想知道回调通常用于什么。

function getDataFromBackend(endPoint, callback) {
  callback(error, data)
}

getDataFromBackend('/api/user', function(error, data) {
  if (error) {
    // handle error - show user error message in ui, etc
  }
   // do something with data - such as display welcome message to user, etc
})

我建议使用回调进行练习。我发现当我使用 Node,或者构建一个带有前端和后端的应用程序时,我会经常实现回调,因为会发生很多异步通信。希望我回答了你的问题。

【讨论】:

  • 感谢您的回复,这很好地解释了我的问题!
【解决方案2】:

首先,您有return someRandomName,但您的参数称为somerandomName。变量区分大小写;这就是为什么您的返回值与您想要的不同。

您的问题是为什么我们不在greet 函数中返回任何内容。答案是“我不知道”。您可以返回一些东西。一些函数返回一些东西;有些功能没有。这与这里的回调安排无关。

function greet(name,callback1){
    return callback1(name)
}
var finalResult = greet("John Cena", function (someRandomName) {
    console.log(someRandomName);
    return someRandomName;
});

现在finalResult 将是"John Cena"

如果它有帮助,那么在您使用匿名函数的任何地方,您都可以轻松地使用命名函数。 (在实践中它通常更丑陋,但为了理解这些概念......)

function greet(name,callback1){
    return callback1(name)
}
function myGreeterFunction(someRandomName) {
    console.log(someRandomName);
    return someRandomName;
});
var finalResult = greet("John Cena", myGreeterFunction);

现在可能更容易看出 callback1(name) 在这种情况下与 myGreeterFunction(name) 相同。

【讨论】:

  • 谢谢,我打错了这个词。我的代码中有更正的拼写,但它仍然返回未定义。但我认为您对使用命名函数的建议比使用匿名函数要好得多。谢谢!
【解决方案3】:

这是一种非常过时的 JavaScript 方法,您完全正确,它违反直觉。在这个特定的例子中,用callback 写这个没有任何优势,可能应该写成一个promise,

greet("John Cena")
    .then(() => {
        return 'Next Action'
    })
    .then(nextAction => {
        console.log(nextAction)
    })

正如@mark-meyer 指出的那样,如果您有异步事件,则需要这种方法。

实际上,AWS Lamda 函数实际上有一个 optional callback 定义为它的第三个参数。 exports.myHandler = function(event, context, callback) {}。老实说,我认为这仅适用于未承诺基于第三方库的情况。

虽然将callback 传递给函数可能永远不是正确的方法,但在某些情况下将function 传递给function 是有价值的。也许在 pub/sub 系统中。

const events = {}

function subscribe (event, func) {
    if (events[event]) {
        events[event].push(func)
    } else {
        events[event] = [func]
    }
}

function publish (event) {
    if (events[events]) {
        events[event].forEach(func => func())
    }
}

如果您使用RamdaJs 中的fp 方法链接进行编写,则链接函数将成为您编写内容的重要组成部分。

@pi2018 是不是有点清楚了?

【讨论】:

  • 感谢您的回复,但我仍然没有阅读有关承诺和 AWS 的内容。
【解决方案4】:

通用技术称为continuation-passing style——与direct style对比。

该技术确实允许出现一些有趣的程序。下面,我们有两个普通函数addmult。我们使用cont 编写了两个程序,它允许我们将这些函数串在一起,其中每个步骤是 前一个的延续。

const cont = x => k =>
  cont (k (x))
  
const add = x => y =>
  x + y
  
const mult = x => y =>
  x * y
  
cont (1) (add (2)) (mult (3)) (console.log) // 9
//    1 ->
//        x => 2 + x
//        cont (3) ->
//                  x => 3 * x
//                  cont (9) ->
//                             x => console.log (x)
//                             cont (undefined)

我们可以根据需要对任意数量的操作进行排序——

const cont = x => k =>
  cont (k (x))
  
const add = x => y =>
  x + y
  
const mult = x => y =>
  x * y

cont (2) (mult (2)) (mult (2)) (mult (2)) (mult (2)) (console.log) // 32
//    2 -> 
//        x => 2 * x
//        cont (4) ->
//                   x => 2 * x
//                   cont (8) ->
//                              x => 2 * x
//                              cont (16) ->
//                                         x => 2 * x
//                                         cont (32) ->
//                                                    x => console.log (x)
//                                                    cont (undefined)

Continuation-passing 风格可用于实现并发程序,但 JavaScript 现在提供了更好的原生并发原语,Promise。甚至还有新的 asyncawait 语法可以让 Promises 的使用更加轻松。

正如维基百科所指出的,编译器更多地使用延续传递样式,而程序员则较少使用。如果您正在尝试编写并发程序,我强烈建议您改用 Promises。 Node 的更高版本包括util.promisify,它允许用户将延续传递样式的函数转换为返回 Promise 的异步函数。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-07-07
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-03-29
    相关资源
    最近更新 更多