gpt4 book ai didi

javascript - 异步调用方法

转载 作者:行者123 更新时间:2023-11-29 15:01:33 25 4
gpt4 key购买 nike

我有一个要在一个方法中调用的方法的列表,如下所示:

this.doOneThing();
someOtherObject.doASecondThing();
this.doSomethingElse();

当这是同步的时,它们一个接一个地执行,这是必需的。但是现在我将someOtherObject.doASecondThing()设为异步,并且我也可能将doOneThing设为异步。我可以使用回调并从回调内部调用that.doSomethingElse:
var that = this;
this.doOneThing( function () {
someOtherObject.doASecondThing(function () {
that.doSomethingElse();
});
});

但是,由于序列在增长,因此彼此之间调用回调似乎有些困惑,由于某种原因,它使序列看起来不像以前那么明显,并且缩进可能随序列中调用的方法的数量而增加。

有没有办法使它看起来更好?我也可以使用观察者模式,但是我认为它也不会很明显。

谢谢,

最佳答案

持续性,以及为什么导致回调意大利面

在回调中编写会迫使您类似于“连续传递样式”(CPS)(一种非常强大但很困难的技术)进行编写。它代表了控制的完全反转,从字面上将计算“颠倒”了。 CPS使您的代码结构明确反射(reflect)程序的控制流(有时是好事,有时是坏事)。实际上,您是在使用匿名函数显式地记录堆栈。

作为理解此答案的先决条件,您可能会发现这很有用:

http://matt.might.net/articles/by-example-continuation-passing-style/

例如,这是您正在做什么:

function thrice(x, ret) {
ret(x*3)
}
function twice(y, ret) {
ret(y*2)
}
function plus(x,y, ret) {
ret(x+y)
}

function threeXPlusTwoY(x,y, ret) {
// STEP#1
thrice(x, // Take the result of thrice(x)...
function(r1) { // ...and call that r1.
// STEP#2
twice(y, // Take the result of twice(y)...
function(r2) { // ...and call that r2.
// STEP#3
plus(r1,r2, // Take r1+r2...
ret // ...then do what we were going to do.
)
}
)
}
)
}

threeXPlusTwoY(5,1, alert); //17

正如您所抱怨的那样,这使代码缩进了很多,因为闭包是捕获此堆栈的自然方法。

救援单子(monad)

使CPS缩进的一种方法是像在Haskell中那样“单调”地编写。我们该怎么做?在javascript中实现monad的一种好方法是使用点链表示法,类似于jQuery。 (有关有趣的转移,请参见 http://importantshock.wordpress.com/2009/01/18/jquery-is-a-monad/。)或者我们可以使用反射。

但是首先我们需要一种“写下管道”的方法,然后我们可以找到一种将其抽象化的方法。可悲的是,用javascript编写通用的monad语法有点困难,因此我将使用列表来表示计算。
// switching this up a bit:
// it's now 3x+2x so we have a diamond-shaped dependency graph

// OUR NEW CODE
var _x = 0;
var steps = [
[0, function(ret){ret(5)},[]], //step0:
[1, thrice,[_x]], //step1: thrice(x)
[2, twice,[_x]], //step2: twice(x)
[3, plus,[1, 2]] //step3: steps[1]+steps[2] *
]
threeXPlusTwoX = generateComputation(steps);

//*this may be left ambiguous, but in this case we will choose steps1 then step2
// via the order in the array

这有点丑陋。但是我们可以使这种UNINDENTED“代码”起作用。我们可能会担心稍后使它更漂亮(在上一节中)。在这里,我们的目的是写下所有“必要信息”。我们希望有一种简单的方法来编写每条“行”,以及可以写入它们的上下文。

现在,我们实现了一个 generateComputation,它生成了一些嵌套的匿名函数,如果执行,它们将按顺序执行上述步骤。这样的实现将如下所示:
function generateComputation(steps) {
/*
* Convert {{steps}} object into a function(ret),
* which when called will perform the steps in order.
* This function will call ret(_) on the results of the last step.
*/
function computation(ret) {
var stepResults = [];

var nestedFunctions = steps.reduceRight(
function(laterFuture, step) {
var i = step[0]; // e.g. step #3
var stepFunction = step[1]; // e.g. func: plus
var stepArgs = step[2]; // e.g. args: 1,2

console.log(i, laterFuture);
return function(returned) {
if (i>0)
stepResults.push(returned);
var evalledStepArgs = stepArgs.map(function(s){return stepResults[s]});
console.log({i:i, returned:returned, stepResults:stepResults, evalledStepArgs:evalledStepArgs, stepFunction:stepFunction});
stepFunction.apply(this, evalledStepArgs.concat(laterFuture));
}
},
ret
);

nestedFunctions();
}
return computation;
}

示范:
threeXPlusTwoX = generateComputation(steps)(alert);  // alerts 25

旁注: reduceRight语义意味着右边的步骤将更深地嵌套在函数中(将来会更进一步)。对于不熟悉的人, [1,2,3].reduce(f(_,_), x) --> f(f(f(0,1), 2), 3)reduceRight(出于不良的设计考虑),仅供引用,实际上相当于 [1.2.3].reversed().reduce(...)
在上面, generateComputation生成了一堆嵌套函数,在准备工作中将它们彼此包装在一起,并在使用 ...(alert)进行评估时,将它们一个一个地剥离,以馈入计算中。

旁注:我们必须使用hack,因为在前面的示例中,我们使用了闭包和变量名称来实现CPS。 Javascript不允许进行充分的反射来执行此操作,而无需诉诸于制作字符串并对其进行 eval编码(点击),因此我们暂时避开了功能样式,并选择了对跟踪所有参数的对象进行变异。因此,以上内容更紧密地复制了以下内容:
var x = 5;
function _x(ret) {
ret(x);
}

function thrice(x, ret) {
ret(x*3)
}
function twice(y, ret) {
ret(y*2)
}
function plus(x,y, ret) {
ret(x+y)
}

function threeXPlusTwoY(x,y, ret) {
results = []
_x(
return function(x) {
results[0] = x;

thrice(x, // Take the result of thrice(x)...
function(r1) { // ...and call that r1.
results[1] = r1;

twice(y, // Take the result of twice(y)...
function(r2) { // ...and call that r2.
results[2] = r2;

plus(results[1],results[2], // Take r1+r2...
ret // ...then do what we were going to do.
)
}
)
}
)

}
)
}

理想语法

但是我们仍然想以一种明智的方式编写函数。我们 理想地是会如何编写代码以利用CPS,同时又保持理智?文献中有很多内容(例如,Scala的 shiftreset运算符只是这样做的众多方法之一),但是出于理智的考虑,让我们找到一种为常规CPS制作语法糖的方法。有一些可能的方法可以做到这一点:
// "bad"
var _x = 0;
var steps = [
[0, function(ret){ret(5)},[]], //step0:
[1, thrice,[_x]], //step1: thrice(x)
[2, twice,[_x]], //step2: twice(x)
[3, plus,[1, 2]] //step3: steps[1]+steps[2] *
]
threeXPlusTwoX = generateComputation(steps);

...成为...
  • 如果回调是链式的,那么我们可以轻松地将回调馈入下一个,而不必担心命名。这些函数只有一个参数:回调参数。 (如果没有,您可以在最后一行中按如下所示处理函数。)在这里,我们可以使用jQuery样式的点链。

  • // SYNTAX WITH A SIMPLE CHAIN
    // ((2*X) + 2)
    twiceXPlusTwo = callbackChain()
    .then(prompt)
    .then(twice)
    .then(function(returned){return plus(returned,2)}); //curried

    twiceXPlusTwo(alert);
  • 如果回调形成一个依赖关系树,我们也可以摆脱jQuery风格的点链,但这将无法为CPS创建单语法的目的,即使嵌套函数变平坦。因此,我们将不在这里详细介绍。
  • 如果回调形成依赖项非循环图(例如2*x+3*x,其中x被使用两次),我们将需要一种方法来命名某些回调的中间结果。这就是它变得有趣的地方。我们的目标是尝试使用do -notation模仿http://en.wikibooks.org/wiki/Haskell/Continuation_passing_style的语法,该符号“解包”和“重新包装”功能进入和退出CPS。不幸的是,[1, thrice,[_x]]语法是我们最容易接近的语法(甚至不是很接近)。您可以使用其他语言编写代码,然后编译为javascript,也可以使用eval(不祥的音乐排队)。有点矫kill过正。替代方案必须使用字符串,例如:

  • // SUPER-NICE SYNTAX
    // (3X + 2X)
    thriceXPlusTwiceX = CPS({
    leftPart: thrice('x'),
    rightPart: twice('x'),
    result: plus('leftPart', 'rightPart')
    })

    您只需对我描述的 generateComputation进行一些调整即可完成此操作。首先,您将其改编为使用逻辑名( 'leftPart'等)而不是数字。然后,使函数实际上是惰性对象,其行为类似于:
    thrice(x).toListForm() == [<real thrice function>, ['x']]
    or
    thrice(x).toCPS()(5, alert) // alerts 15
    or
    thrice.toNonCPS()(5) == 15

    (您可以使用某种装饰器以自动化方式执行此操作,而不是手动执行此操作。)

    旁注:所有回调函数都应遵循有关回调参数位置的相同协议(protocol)。例如,如果您的函数以 myFunction(callback, arg0, arg1, ...)myFunction(arg0, arg1, ..., callback)开头,则它们可能并不完全兼容,尽管如果它们可能不兼容,您可以执行javascript反射hack来查看函数的源代码并对其进行正则表达式,因此不必担心它。

    为什么要经历所有麻烦?这使您可以混合使用 setTimeoutprompt和ajax请求,而不会遭受“缩进 hell ”的麻烦。您还将获得很多其他好处(例如能够编写10行非确定性搜索数独解算器,以及实现任意控制流运算符),在此我将不再赘述。

    关于javascript - 异步调用方法,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/9433725/

    25 4 0
    Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
    广告合作:1813099741@qq.com 6ren.com