jjzjj

javascript - "Phased"在javascript中执行函数

coder 2025-02-05 原文

这是我在 stackoverflow 上的第一篇文章,所以如果我遇到一个彻头彻尾的傻瓜,或者如果我无法让自己完全清楚,请不要对我发火。 :-)

这是我的问题:我正在尝试编写一个 javascript 函数,通过检查第一个函数的完成然后执行第二个函数来将两个函数“绑定(bind)”到另一个函数。

显然,解决这个问题的简单方法是编写一个元函数,在其范围内调用这两个函数。但是,如果第一个函数是异步的(特别是 AJAX 调用),而第二个函数需要第一个函数的结果数据,那根本行不通。

我对解决方案的想法是给第一个函数一个“标志”,即一旦调用它就创建一个公共(public)属性“this.trigger”(初始化为“0”,完成后设置为“1”);这样做应该可以让另一个函数检查标志的值([0,1])。如果满足条件(“trigger == 1”),则应调用第二个函数。

以下是我用于测试的抽象示例代码:

<script type="text/javascript" >

/**/function cllFnc(tgt) { //!! first function

    this.trigger = 0 ;
    var trigger = this.trigger ;

    var _tgt = document.getElementById(tgt) ; //!! changes the color of the target div to signalize the function's execution
        _tgt.style.background = '#66f' ;

    alert('Calling! ...') ;

    setTimeout(function() { //!! in place of an AJAX call, duration 5000ms

            trigger = 1 ;

    },5000) ;

}

/**/function rcvFnc(tgt) { //!! second function that should get called upon the first function's completion

    var _tgt = document.getElementById(tgt) ; //!! changes color of the target div to signalize the function's execution
        _tgt.style.background = '#f63' ;

    alert('... Someone picked up!') ;

}

/**/function callCheck(obj) {   

            //alert(obj.trigger ) ;      //!! correctly returns initial "0"                         

    if(obj.trigger == 1) {              //!! here's the problem: trigger never receives change from function on success and thus function two never fires 

                        alert('trigger is one') ;
                        return true ;
                    } else if(obj.trigger == 0) {
                        return false ;
                    }


}

/**/function tieExc(fncA,fncB,prms) {

        if(fncA == 'cllFnc') {
            var objA = new cllFnc(prms) ;   
            alert(typeof objA + '\n' + objA.trigger) ;  //!! returns expected values "object" and "0"
        } 

        //room for more case definitions

    var myItv = window.setInterval(function() {

        document.getElementById(prms).innerHTML = new Date() ; //!! displays date in target div to signalize the interval increments


        var myCallCheck = new callCheck(objA) ; 

            if( myCallCheck == true ) { 

                    if(fncB == 'rcvFnc') {
                        var objB = new rcvFnc(prms) ;
                    }

                    //room for more case definitions

                    window.clearInterval(myItv) ;

            } else if( myCallCheck == false ) {
                return ;
            }

    },500) ;

}

</script>

用于测试的 HTML 部分:
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/strict.dtd >

<html>

<head>

    <script type="text/javascript" >
       <!-- see above -->
    </script>

    <title>

      Test page

    </title>


</head>

<body>

    <!-- !! testing area -->

        <div id='target' style='float:left ; height:6em ; width:8em ; padding:0.1em 0 0 0; font-size:5em ; text-align:center ; font-weight:bold ; color:#eee ; background:#fff;border:0.1em solid #555 ; -webkit-border-radius:0.5em ;' >
            Test Div
        </div>

        <div style="float:left;" >
            <input type="button" value="tie calls" onmousedown="tieExc('cllFnc','rcvFnc','target') ;" />
        </div>

<body>


</html>

我很确定这是javascript范围的一些问题,因为我已经检查了触发器是否正确设置为“1”并且确实如此。 “checkCall()”函数很可能没有接收到更新的对象,而是只检查它的旧实例,显然不会通过将“this.trigger”设置为“1”来标记完成。如果是这样,我不知道如何解决这个问题。

无论如何,希望有人对这种特殊问题有想法或经验。

谢谢阅读!

FK

最佳答案

您可以利用 JS 的一个称为闭包的功能。将它与一种非常常见的 JS 模式相结合,称为“持续传递样式”,您就有了解决方案。 (这些东西都不是 JS 的原创,但在 JS 中被大量使用)。

// a function
function foo(some_input_for_foo, callback)
{
    // do some stuff to get results

    callback(results); // call our callback when finished
}

// same again
function bar(some_input_for_bar, callback)
{
    // do some stuff to get results

    callback(results); // call our callback when finished
}

“延续传递风格”是指回调。每个函数不是返回一个值,而是调用一个回调(延续)并给它结果。

然后,您可以轻松地将两者联系在一起:
foo(input1, function(results1) {

    bar(results1, function(results2) {

        alert(results2);
    });
});

嵌套的匿名函数可以从它们所在的范围内“看到”变量。因此无需使用特殊属性来传递信息。

更新

为了澄清,在您的问题的代码片段中,很明显您的想法大致是这样的:

I have a long-running asynchronous operation, so I need to know when it finishes in order to start the next operation. So I need to make that state visible as a property. Then elsewhere I can run in a loop, repeatedly examining that property to see when it changes to the "completed" state, so I know when to continue.



(然后作为一个复杂因素,循环必须使用 setInterval 开始运行并使用 clearInterval 退出,以允许其他 JS 代码运行 - 但它基本上是一个“轮询循环”)。

你不需要这样做!

不要让你的第一个函数在完成时设置一个属性,而是让它调用一个函数。

为了清楚地说明这一点,让我们重构您的原始代码:
function cllFnc(tgt) { //!! first function

    this.trigger = 0 ;
    var trigger = this.trigger ;

    var _tgt = document.getElementById(tgt) ; //!! changes the color...
    _tgt.style.background = '#66f' ;

    alert('Calling! ...') ;

    setTimeout(function() { //!! in place of an AJAX call, duration 5000ms

        trigger = 1 ;

    },5000) ;
}

[ 更新 2 : 顺便说一句,那里有一个错误。您复制 trigger 的当前值属性到一个名为 trigger 的新局部变量中.然后最后将 1 分配给该局部变量。没有其他人能够看到这一点。局部变量是函数私有(private)的。但无论如何你都不需要这样做,所以请继续阅读......]

我们所要做的就是告诉该函数在完成后调用什么,并摆脱属性设置:
function cllFnc(tgt, finishedFunction) { //!! first function

    var _tgt = document.getElementById(tgt) ; //!! changes the color...
    _tgt.style.background = '#66f' ;

    alert('Calling! ...') ;

    setTimeout(function() { //!! in place of an AJAX call, duration 5000ms

        finishedFunction(); // <-------- call function instead of set property

    },5000) ;
}

现在不需要您的“电话检查”或您的特殊 tieExc helper 。只需很少的代码,您就可以轻松地将两个函数绑定(bind)在一起。
var mySpan = "#myspan";

cllFnc(mySpan, function() { rcvFnc(mySpan); });

这样做的另一个好处是我们可以将不同的参数传递给第二个函数。使用您的方法,将相同的参数传递给两者。

例如,第一个函数可能会调用 AJAX 服务(为简洁起见使用 jQuery):
function getCustomerBillAmount(name, callback) {

    $.get("/ajax/getCustomerIdByName/" + name, function(id) {

        $.get("/ajax/getCustomerBillAmountById/" + id), callback);

    });
}

在这里,callback接受客户账单金额,AJAX get call 将接收到的值传递给我们传递它的函数,所以 callback已经兼容,因此可以直接充当第二个 AJAX 调用的回调。因此,这本身就是将两个异步调用按顺序捆绑在一起并将它们包装在(从外部)看起来是单个异步函数的示例。

然后我们可以将它与另一个操作链接起来:
function displayBillAmount(amount) {

    $("#billAmount").text(amount); 
}

getCustomerBillAmount("Simpson, Homer J.", displayBillAmount);

或者我们可以(再次)使用匿名函数:
getCustomerBillAmount("Simpson, Homer J.", function(amount) {

    $("#billAmount").text(amount); 
});

因此,通过像这样链接函数调用,每个步骤都可以在信息可用时立即将信息传递到下一步。

通过让函数在完成时执行回调,您可以摆脱对每个函数内部工作方式的任何限制。它可以做 AJAX 调用、定时器等等。只要“继续”回调向前传递,就可以有任意数量的异步工作层。

基本上,在异步系统中,如果您发现自己编写了一个循环来检查变量并确定它是否改变了状态,那么某处出现了问题。相反,应该有一种方法来提供将在状态更改时调用的函数。

更新 3

我在评论的其他地方看到你提到实际问题是缓存结果,所以我解释这一切的所有工作都是浪费时间。这是你应该提出的问题。

更新 4

最近我写了a short blog post on the subject of caching asynchronous call results in JavaScript .

(更新 4 结束)

共享结果的另一种方法是提供一种方法,让一个回调“广播”或“发布”给多个订阅者:
function pubsub() {
    var subscribers = [];

    return {
        subscribe: function(s) {
            subscribers.push(s);
        },
        publish: function(arg1, arg2, arg3, arg4) {
            for (var n = 0; n < subscribers.length; n++) {
                subscribers[n](arg1, arg2, arg3, arg4);
            }
        }
    };
}

所以:
finished = pubsub();

// subscribe as many times as you want:

finished.subscribe(function(msg) {
    alert(msg);
});

finished.subscribe(function(msg) {
    window.title = msg;
});

finished.subscribe(function(msg) {
    sendMail("admin@mysite.com", "finished", msg);
});

然后让一些缓慢的操作发布它的结果:
lookupTaxRecords("Homer J. Simpson", finished.publish);

当一个调用结束时,它现在将调用所有三个订阅者。

关于javascript - "Phased"在javascript中执行函数,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/2552792/

有关javascript - "Phased"在javascript中执行函数的更多相关文章

  1. ruby-on-rails - rails : "missing partial" when calling 'render' in RSpec test - 2

    我正在尝试测试是否存在表单。我是Rails新手。我的new.html.erb_spec.rb文件的内容是:require'spec_helper'describe"messages/new.html.erb"doit"shouldrendertheform"dorender'/messages/new.html.erb'reponse.shouldhave_form_putting_to(@message)with_submit_buttonendendView本身,new.html.erb,有代码:当我运行rspec时,它失败了:1)messages/new.html.erbshou

  2. ruby-on-rails - 由于 "wkhtmltopdf",PDFKIT 显然无法正常工作 - 2

    我在从html页面生成PDF时遇到问题。我正在使用PDFkit。在安装它的过程中,我注意到我需要wkhtmltopdf。所以我也安装了它。我做了PDFkit的文档所说的一切......现在我在尝试加载PDF时遇到了这个错误。这里是错误:commandfailed:"/usr/local/bin/wkhtmltopdf""--margin-right""0.75in""--page-size""Letter""--margin-top""0.75in""--margin-bottom""0.75in""--encoding""UTF-8""--margin-left""0.75in""-

  3. ruby-openid:执行发现时未设置@socket - 2

    我在使用omniauth/openid时遇到了一些麻烦。在尝试进行身份验证时,我在日志中发现了这一点:OpenID::FetchingError:Errorfetchinghttps://www.google.com/accounts/o8/.well-known/host-meta?hd=profiles.google.com%2Fmy_username:undefinedmethod`io'fornil:NilClass重要的是undefinedmethodio'fornil:NilClass来自openid/fetchers.rb,在下面的代码片段中:moduleNetclass

  4. ruby - 检查 "command"的输出应该包含 NilClass 的意外崩溃 - 2

    为了将Cucumber用于命令行脚本,我按照提供的说明安装了arubagem。它在我的Gemfile中,我可以验证是否安装了正确的版本并且我已经包含了require'aruba/cucumber'在'features/env.rb'中为了确保它能正常工作,我写了以下场景:@announceScenario:Testingcucumber/arubaGivenablankslateThentheoutputfrom"ls-la"shouldcontain"drw"假设事情应该失败。它确实失败了,但失败的原因是错误的:@announceScenario:Testingcucumber/ar

  5. ruby - 在没有 sass 引擎的情况下使用 sass 颜色函数 - 2

    我想在一个没有Sass引擎的类中使用Sass颜色函数。我已经在项目中使用了sassgem,所以我认为搭载会像以下一样简单:classRectangleincludeSass::Script::FunctionsdefcolorSass::Script::Color.new([0x82,0x39,0x06])enddefrender#hamlengineexecutedwithcontextofself#sothatwithintemlateicouldcall#%stop{offset:'0%',stop:{color:lighten(color)}}endend更新:参见上面的#re

  6. ruby - Chef 执行非顺序配方 - 2

    我遵循了教程http://gettingstartedwithchef.com/,第1章。我的运行list是"run_list":["recipe[apt]","recipe[phpap]"]我的phpapRecipe默认Recipeinclude_recipe"apache2"include_recipe"build-essential"include_recipe"openssl"include_recipe"mysql::client"include_recipe"mysql::server"include_recipe"php"include_recipe"php::modul

  7. ruby-on-rails - 迷你测试错误 : "NameError: uninitialized constant" - 2

    我遵循MichaelHartl的“RubyonRails教程:学习Web开发”,并创建了检查用户名和电子邮件长度有效性的测试(名称最多50个字符,电子邮件最多255个字符)。test/helpers/application_helper_test.rb的内容是:require'test_helper'classApplicationHelperTest在运行bundleexecraketest时,所有测试都通过了,但我看到以下消息在最后被标记为错误:ERROR["test_full_title_helper",ApplicationHelperTest,1.820016791]test

  8. ruby-on-rails - 在 ruby​​ 中使用 gsub 函数替换单词 - 2

    我正在尝试用ruby​​中的gsub函数替换字符串中的某些单词,但有时效果很好,在某些情况下会出现此错误?这种格式有什么问题吗NoMethodError(undefinedmethod`gsub!'fornil:NilClass):模型.rbclassTest"replacethisID1",WAY=>"replacethisID2andID3",DELTA=>"replacethisID4"}end另一个模型.rbclassCheck 最佳答案 啊,我找到了!gsub!是一个非常奇怪的方法。首先,它替换了字符串,所以它实际上修改了

  9. ruby-on-rails - 相关表上的范围为 "WHERE ... LIKE" - 2

    我正在尝试从Postgresql表(table1)中获取数据,该表由另一个相关表(property)的字段(table2)过滤。在纯SQL中,我会这样编写查询:SELECT*FROMtable1JOINtable2USING(table2_id)WHEREtable2.propertyLIKE'query%'这工作正常:scope:my_scope,->(query){includes(:table2).where("table2.property":query)}但我真正需要的是使用LIKE运算符进行过滤,而不是严格相等。然而,这是行不通的:scope:my_scope,->(que

  10. 使用 ACL 调用 upload_file 时出现 Ruby S3 "Access Denied"错误 - 2

    我正在尝试编写一个将文件上传到AWS并公开该文件的Ruby脚本。我做了以下事情:s3=Aws::S3::Resource.new(credentials:Aws::Credentials.new(KEY,SECRET),region:'us-west-2')obj=s3.bucket('stg-db').object('key')obj.upload_file(filename)这似乎工作正常,除了该文件不是公开可用的,而且我无法获得它的公共(public)URL。但是当我登录到S3时,我可以正常查看我的文件。为了使其公开可用,我将最后一行更改为obj.upload_file(file

随机推荐