使用回调参数获取匿名函数中的值

Using a callback parameter to get the value in the Anonymous Function

我发现了以下回调函数代码。我理解代码,但我无法理解它。这看起来很违反直觉。

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 函数中没有 return 任何东西,为什么?

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

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

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

你的问题是为什么我们不在 greet 函数中 return 任何东西。答案是 "I have no idea." 你 可以 return 一些东西。一些功能return的东西;有些功能没有。这与这里的回调安排无关。

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) 是一回事。

这是 JavaScript 的一种非常过时的方法,您完全正确,它有悖于直觉。在这个特定的例子中,用 callback 写这个没有任何好处,可能应该写成一个承诺,

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())
    }
}

如果您使用 fp approach link in RamdaJs 编写,链接函数将占您编写内容的很大一部分。

@pi2018 是不是有点搞清楚了?

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

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

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

像您一样添加 return 并没有多大作用。为了实际 return 名称值,您必须像这样编写原始函数。

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 等库来完成。请记住,第一个调用端点的函数需要一些时间来执行和获取数据。在数据被 returned 之前,回调不会触发。当然,后端会有一个函数将错误或数据发送回回调。我不想让事情过于复杂,而只是想知道回调通常用于什么。

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,或者构建一个带有前端和后端的应用程序时,我会实现很多回调,因为会发生很多异步通信。希望我回答了你的问题。

一般技术称为continuation-passing style – contrast with 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 返回异步函数。