有人能给出一个明确的定义和一个简单的例子来解释什么是不懂JavaScript和node.js的人的“回调地狱”吗?
什么时候(在什么样的设置中)会出现“回调地狱问题”?
为什么会发生这种情况?
“回调地狱”总是与异步计算相关吗?
或者“回调地狱”也会出现在单个线程应用程序中吗?
我参加了古瑟拉的反应课程,埃里克·梅耶尔在他的一次讲座中说,RX解决了“回调地狱”的问题。我问古瑟尔论坛上的“回调地狱”是什么,但我没有得到明确的答案。
在一个简单的例子中解释了“回调地狱”之后,你还能展示RX如何解决这个简单例子上的“回调地狱问题”吗?
发布于 2014-08-02 18:38:36
1)对于不懂javascript和node.js的人来说,什么是“回调地狱”?
另一个问题有一些Javascript回调地狱的例子:如何避免Node.js中异步函数的长嵌套
Javascript中的问题是,“冻结”计算并让“其余部分”执行后者(异步)的唯一方法是将“其余部分”放入回调中。
例如,假设我想运行如下代码:
x = getData();
y = getMoreData(x);
z = getMoreData(y);
...如果现在我想使getData函数异步化,这意味着我有机会在等待它们返回值时运行其他代码,那么会发生什么呢?在Javascript中,唯一的方法是使用延拓传递方式重写所有涉及异步计算的内容。
getData(function(x){
getMoreData(x, function(y){
getMoreData(y, function(z){
...
});
});
});我不认为我需要让任何人相信这个版本比前一个版本更丑。:-)
2)“回调地狱问题”何时(在何种情况下)发生?
当您的代码中有大量回调函数时!在代码中使用它们的难度越大,当您需要执行循环、尝试捕获块等操作时,情况就会变得特别糟糕。
例如,据我所知,在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)单线程应用程序是否也会出现“回调地狱”?
异步编程与并发性有关,而单线程则与并行性有关。这两个概念实际上不是一回事。
您仍然可以在单个线程上下文中拥有并发代码。事实上,JavaScript,回调地狱的女王,是单线程的。
5)请您也展示一下RX如何在这个简单的例子中解决“回调地狱问题”。
我对RX并不了解,但通常通过在编程语言中添加对异步计算的本地支持来解决这个问题。实现可以不同,包括:异步、生成器、协同器和callcc。
在Python中,我们可以使用以下内容来实现前面的循环示例:
def myLoop():
for i in range(10):
doSomething(i)
yield
myGen = myLoop()这不是完整的代码,但思想是,“产生”暂停我们的for循环,直到有人调用myGen.next()。重要的是,我们仍然可以使用for循环编写代码,而不需要像在递归loop函数中所做的那样,将逻辑“内翻”。
发布于 2017-06-14 12:25:43
解决Rx如何解决回调地狱的问题
首先,让我们再来描述一下回调地狱。
想象一下,如果我们必须做http来获得三种资源--人、行星和星系。我们的目标是找到那个人居住的星系。首先我们必须找到那个人,然后是行星,然后是银河系。这是三个异步操作的三个回调。
getPerson(person => {
getPlanet(person, (planet) => {
getGalaxy(planet, (galaxy) => {
console.log(galaxy);
});
});
});每个回调都是嵌套的。每个内部回调都依赖于其父级。这导致了“厄运金字塔”式的回调地狱。代码看起来像是一个>符号。
要在RxJ中解决这个问题,您可以这样做:
getPerson()
.map(person => getPlanet(person))
.map(planet => getGalaxy(planet))
.mergeAll()
.subscribe(galaxy => console.log(galaxy));使用mergeMap AKA flatMap运算符,您可以使其更加简洁:
getPerson()
.mergeMap(person => getPlanet(person))
.mergeMap(planet => getGalaxy(planet))
.subscribe(galaxy => console.log(galaxy));如您所见,代码是扁平的,包含一个方法调用链。我们没有“末日金字塔”。
因此,避免了回调地狱。
如果你想知道,承诺是另一种避免回调地狱的方法,但是承诺是热切的,不像可观察的那样懒惰,而且(一般来说)你不能轻易地取消它们。
发布于 2014-08-04 01:20:14
只需回答以下问题:请您也展示一下RX如何在这个简单的例子中解决“回调地狱问题”吗?
神奇的是flatMap。我们可以在Rx中为@hugomg的示例编写以下代码:
def getData() = Observable[X]
getData().flatMap(x -> Observable[Y])
.flatMap(y -> Observable[Z])
.map(z -> ...)...这就好像您正在编写一些同步FP代码,但实际上您可以通过Scheduler使它们异步。
https://stackoverflow.com/questions/25098066
复制相似问题