Go语言有回调概念吗?

huangapple go评论91阅读模式
英文:

Does Go have callback concept?

问题

我发现很多讨论都说Node.js因为回调地狱而不好,而Go语言因为其同步模型而好。

我感觉Go语言也可以像Node.js一样使用回调,但是以同步的方式。因为我们可以传递匿名函数并进行闭包操作

那么,为什么他们在回调方面比较Go和Node.js,好像Go不能出现回调地狱一样。

或者我对Go中的回调和匿名函数的含义有误解?

英文:

I found many talks saying that Node.js is bad because of callback hell and Go is good because of its synchronous model.

What I feel is Go can also do callback as same as Node.js but in a synchronous way. As we can pass anonymous function and do closure things

So, why are they comparing Go and Node.js in callback perspective as if Go cannot become callback hell.

Or I misunderstand the meaning of callback and anonymous function in Go?

答案1

得分: 46

很多事情需要时间,比如等待网络套接字、文件系统读取、系统调用等。因此,很多编程语言,更准确地说是它们的标准库,都包含了异步版本的函数(通常是在同步版本的基础上),这样你的程序就能在等待期间做其他事情。

在Node.js中,情况更加极端。它们使用单线程的事件循环,因此需要确保你的程序永远不会阻塞。Node.js有一个非常完善的标准库,围绕异步概念构建,并使用回调函数来通知你何时准备就绪。代码基本上是这样的:

doSomething1(arg1, arg2, function() {
  doSomething2(arg1, arg2, function() {
    doSomething3(function() {
      // 完成
    });
  });
});
somethingElse();

doSomething1可能需要很长时间才能执行(例如,因为它需要从网络读取数据),但你的程序仍然可以在此期间执行somethingElse。在doSomething1执行完毕后,你希望调用doSomething2doSomething3

另一方面,Go基于goroutine和channel的概念(如果你想了解更多关于抽象概念的内容,请搜索“通信顺序进程”)。Goroutine非常轻量级(你可以同时运行数千个),因此你可以在任何地方使用它们。在Go中,相同的代码可能如下所示:

go func() {
  doSomething1(arg1, arg2)
  doSomething2(arg1, arg2)
  doSomething3()
  // 完成
}()
somethingElse()

虽然Node.js专注于提供异步API,但Go通常鼓励你只编写同步API(不使用回调函数或通道)。对doSomething1的调用将阻塞当前的goroutine,只有在doSomething1执行完毕后,才会执行doSomething2。但在Go中,这并不是问题,因为通常有其他可调度到系统线程上运行的goroutine。在这种情况下,somethingElse是另一个goroutine的一部分,可以在此期间执行,就像Node.js示例中的情况一样。

我个人更喜欢Go代码,因为它更易于阅读和理解。Go的另一个优点是它也适用于计算密集型任务。如果你在Node.js中启动一个不需要等待网络或文件系统调用的大量计算,这个计算会阻塞你的事件循环。而Go的调度器会尽力将goroutine分派到少量的系统线程上运行,如果你的CPU支持,操作系统可能会并行运行这些线程。

英文:

A lot of things take time, e.g. waiting on a network socket, a file system read, a system call, etc. Therefore, a lot of languages, or more precisely their standard library, include asynchronous version of their functions (often in addition to the synchronous version), so that your program is able to do something else in the mean-time.

In node.js things are even more extreme. They use a single-threaded event loop and therefore need to ensure that your program never blocks. They have a very well written standard library that is built around the concept of being asynchronous and they use callbacks in order to notify you when something is ready. The code basically looks like this:

doSomething1(arg1, arg2, function() {
  doSomething2(arg1, arg2, function() {
    doSomething3(function() {
      // done
    });
  });
});
somethingElse();

doSomething1 might take a long time to execute (because it needs to read from the network for example), but your program can still execute somethingElse in the mean time. After doSomething1 has been executed, you want to call doSomething2 and doSomething3.

Go on the other hand is based around the concept of goroutines and channels (google for "Communicating Sequential Processes", if you want to learn more about the abstract concept). Goroutines are very cheap (you can have several thousands of them running at the same time) and therefore you can use them everywhere. The same code might look like this in Go:

go func() {
  doSomething1(arg1, arg2)
  doSomething2(arg1, arg2)
  doSomething3()
  // done
}()
somethingElse()

Whereas node.js focus on providing only asynchronous APIs, Go usually encourages you to write only synchronous APIs (without callbacks or channels). The call to doSomething1 will block the current goroutine and doSomething2 will only be executed after doSomething1 has finished. But that's not a problem in Go, since there are usually other goroutines available that can be scheduled to run on the system thread. In this case, somethingElse is part of another goroutine and can be executed in the meantime, just like in the node.js example.

I personally prefer the Go code, since it's easier to read and reason about. Another advantage of Go is that it also works well with computation heavy tasks. If you start a heavy computation in node.js that doesn't need to wait for network of filesystem calls, this computation basically blocks your event loop. Go's scheduler on the other hand will do its best to dispatch the goroutines on a few number of system threads and the OS might run those threads in parallel if your CPU supports it.

答案2

得分: 11

当然,Go语言也可以像Node.js一样使用回调,但是以同步的方式进行。我们可以传递匿名函数并进行闭包操作。

所以,为什么他们在比较Go语言和Node.js的回调方面时,好像在暗示Go语言不能陷入回调地狱呢?

是的,当然在Go语言中也有可能搞砸事情。之所以不像Node.js那样频繁地使用回调,是因为Go语言有通道(channels)用于通信,这种方式可以在不使用回调的情况下组织代码结构。

因此,由于有通道的存在,回调不经常使用,因此很少会遇到回调陷阱的代码。当然,这并不意味着你不能使用通道编写令人恐惧的代码...

英文:

> What I feel is Golang can also do callback as same as Node.js but in a synchronous way. As we can pass anonymous function and do closure things
>
> So, why are they comparing Golang and Node.js in callback perspective as if Golang cannot become callback hell.

Yes, of course it is possible to mess things up in Go as well. The reason why you don't see as much callbacks as in node.js is that Go has channels for communication, which allow for a way of structuring your code without using callbacks.

So, since there are channels, callbacks are not used as often therefore it is unlikely to stumble over callback infested code. Of course this doesn't mean that you cannot write scary code with channels as well...

huangapple
  • 本文由 发表于 2014年5月17日 16:15:25
  • 转载请务必保留本文链接:https://go.coder-hub.com/23709118.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定