有人可以给出一个清晰的定义,并给出一个简单的示例,为不懂JavaScript和node.js的人解释什么是“回调 hell ”吗?
什么时候(以哪种设置)发生“回调 hell 问题”?
为什么会发生?
“回调 hell ”是否总是与异步计算相关?
还是在单线程应用程序中也可能发生“回调 hell ”?
我在Coursera参加了Reactive Course,Erik Meijer在他的一次演讲中说RX解决了“回调 hell ”的问题。我在Coursera论坛上问什么是“回调 hell ”,但我没有明确的答案。
在一个简单示例中解释了“回调 hell ”之后,您还可以说明该简单示例中RX如何解决“回调 hell 问题”吗?
最佳答案
1)对于不了解javascript和node.js的人来说,什么是“回调 hell ”?
另外一个问题有一些Javascript回调 hell 的示例:How to avoid long nesting of asynchronous functions in Node.js
Javascript中的问题在于,“冻结”计算并使“其余部分”(异步地)执行的唯一方法是将“其余部分”放入回调中。
例如,假设我要运行如下代码:
x = getData();
y = getMoreData(x);
z = getMoreData(y);
...
如果现在我想使getData函数异步,这意味着我有机会在等待其他代码返回值时运行一些其他代码,会发生什么情况?在Javascript中,唯一的方法是使用continuation passing style重写所有涉及异步计算的内容:
getData(function(x){
getMoreData(x, function(y){
getMoreData(y, function(z){
...
});
});
});
我认为我不需要说服任何人此版本比以前的版本丑陋。 :-)
2)什么时候(以哪种设置)发生“回调 hell 问题”?
当您的代码中有很多回调函数时!在代码中包含的代码越多,与他们合作的难度就越大,当您需要执行循环,try-catch块之类的事情时,情况就变得尤为糟糕。
例如,据我所知,在JavaScript中执行一系列异步函数的唯一方法是使用递归函数,该异步函数在先前的返回值之后运行。您不能使用for循环。
// we would like to write the following
for(var i=0; i<10; i++){
doSomething(i);
}
blah();
相反,我们可能需要结束编写:
function loop(i, onDone){
if(i >= 10){
onDone()
}else{
doSomething(i, function(){
loop(i+1, onDone);
});
}
}
loop(0, function(){
blah();
});
//ugh!
我们在StackOverflow上收到的许多问题询问如何做这种事情,这证明了它是多么令人困惑:)
3)为什么会发生?
发生这种情况的原因是,在JavaScript中,延迟计算以使其在异步调用返回之后运行的唯一方法是将延迟的代码放入回调函数中。您不能延迟以传统同步方式编写的代码,因此最终到处都有嵌套的回调。
4)还是在单线程应用程序中也可能发生“回调 hell ”?
异步编程与并发有关,而单线程与并行性有关。这两个概念实际上不是一回事。
您仍然可以在单个线程上下文中拥有并发代码。实际上,JavaScript是回调 hell 的女王,是单线程的。
What is the difference between concurrency and parallelism?
5)您能否也请通过这个简单示例说明RX如何解决“回调 hell 问题”。
我对RX一点都不了解,但是通常可以通过在编程语言中添加对异步计算的 native 支持来解决此问题。实现可能有所不同,包括:异步,生成器,协程和callcc。
在Python中,我们可以使用类似于以下内容的代码来实现该先前的循环示例:
def myLoop():
for i in range(10):
doSomething(i)
yield
myGen = myLoop()
这不是完整的代码,但是其思想是“yield”暂停我们的for循环,直到有人调用myGen.next()。重要的是,我们仍然可以使用for循环来编写代码,而无需像在递归
loop
函数中那样进行“由内而外”的逻辑。