“分阶段” javascript中函数的执行

发布于 2024-08-27 02:00:13 字数 3727 浏览 6 评论 0原文

这是我在 stackoverflow 上的第一篇文章,所以如果我看起来像个白痴或者我无法完全清楚地表达自己,请不要太严厉地攻击我。 :-)

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

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

我的解决方案的想法是给第一个函数一个“标志”,即让它在调用后创建一个公共属性“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”来标记完成。如果是这样,我不知道如何解决这个问题。

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

感谢您的阅读!

氟肯

This is my first post on stackoverflow, so please don't flame me too hard if I come across like a total nitwit or if I'm unable ot make myself perfectly clear. :-)

Here's my problem: I'm trying to write a javascript function that "ties" two functions to another by checking the first one's completion and then executing the second one.

The easy solution to this obviously would be to write a meta function that calls both functions within it's scope. However, if the first function is asynchronous (specifically an AJAX call) and the second function requires the first one's result data, that simply won't work.

My idea for a solution was to give the first function a "flag", i.e. making it create a public property "this.trigger" (initialized as "0", set to "1" upon completion) once it is called; doing that should make it possible for another function to check the flag for its value ([0,1]). If the condition is met ("trigger == 1") the second function should get called.

The following is an abstract example code that I have used for testing:

<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>

The HTML part for testing:

<!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>

I'm pretty sure that this is some issue with javascript scope as I have checked whether the trigger gets set to "1" correctly and it does. Very likely the "checkCall()" function does not receive the updated object but instead only checks its old instance which obviously never flags completion by setting "this.trigger" to "1". If so I don't know how to address that issue.

Anyway, hope someone has an idea or experience with this particular kind of problem.

Thanks for reading!

FK

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(5

瞄了个咪的 2024-09-03 02:00:13

您可以利用 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);
    });
});

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

更新

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

我有一个长时间运行的异步
操作,所以我需要知道什么时候
完成以便开始下一个
手术。所以我需要这样做
状态作为属性可见。然后
在其他地方我可以循环运行,
反复检查该财产
看看它什么时候变成“已完成”
状态,以便我知道何时继续。

(然后,作为一个复杂的因素,循环必须使用 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 赋给该局部变量。没有其他人能够看到这一点。局部变量是函数私有的。 但是你不需要做任何这些事情,所以继续阅读...]

我们所要做的就是告诉该函数在完成后调用什么,并摆脱该属性 -设置:

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 帮助程序。您可以使用很少的代码轻松地将两个函数绑定在一起。

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 将接收到的值传递给我们传递的函数,因此回调已经兼容,因此可以直接充当第二个 AJAX 调用的回调。因此,这本身就是一个将两个异步调用按顺序绑定在一起并将它们包装在(从外部)看起来像单个异步函数的示例。

然后我们可以将其与另一个操作链接起来:

function displayBillAmount(amount) {

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

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

或者我们可以(再次)使用匿名函数:

getCustomerBillAmount("Simpson, Homer J.", function(amount) {

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

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

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

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

更新 3

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

更新4

最近我写了一篇关于在 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("[email protected]", "finished", msg);
});

然后让一些缓慢的操作发布其结果:

lookupTaxRecords("Homer J. Simpson", finished.publish);

当一个呼叫结束后,它现在将呼叫所有三个订阅者。

You can take advantage of a feature of JS called closure. Combine that with a very common JS pattern called "continuation passing style" and you have your solution. (Neither of these things are original to JS, but are heavily used in 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
}

The "continuation passing style" refers to the callback. Instead of returning a value, each function calls a callback (the continuation) and gives it the results.

You can then tie the two together easily:

foo(input1, function(results1) {

    bar(results1, function(results2) {

        alert(results2);
    });
});

The nested anonymous functions can "see" variables from the scope they live in. So there's no need to use special properties to pass information around.

Update

To clarify, in your question's code snippet, it's clear that you are thinking roughly like this:

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.

(And then as a complicating factor, the loop has to use setInterval to start running and clearInterval to quit, to allow other JS code to run - but it's basically a "polling loop" nevertheless).

You do not need to do that!

Instead of making your first function set a property on completion, make it call a function.

To make this absolutely clear, let's refactor your original code:

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) ;
}

[Update 2: By the way, there's a bug there. You copy the current value of the trigger property into a new local variable called trigger. Then at the end you assign 1 to that local variable. No one else is going to be able to see that. Local variables are private to a function. But you don't need to do any of this anyway, so keep reading...]

All we have to do is tell that function what to call when it's done, and get rid of the property-setting:

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) ;
}

There's now no need for your "call-check" or your special tieExc helper. You can easily tie two functions together with very little code.

var mySpan = "#myspan";

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

Another advantage of this is that we can pass different parameters to the second function. With your approach, the same parameters are passed to both.

For example, the first function might do a couple of calls to an AJAX service (using jQuery for brevity):

function getCustomerBillAmount(name, callback) {

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

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

    });
}

Here, callback accepts the customer bill amount, and the AJAX get call passes the received value to the function we pass it, so the callback is already compatible and so can directly act as the callback for the second AJAX call. So this is itself an example of tying two asynchronous calls together in sequence and wrapping them in what appears (from the outside) to be a single asynchronous function.

Then we can chain this with another operation:

function displayBillAmount(amount) {

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

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

Or we could (again) have used an anonymous function:

getCustomerBillAmount("Simpson, Homer J.", function(amount) {

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

So by chaining function calls like this, each step can pass information forward to the next step as soon as it is available.

By making functions execute a callback when they're done, you are freed from any limitations to how each functions works internally. It can do AJAX calls, timers, whatever. As long as the "continuation" callback is passed forward, there can be any number of layers of asynchronous work.

Basically, in an asynchronous system, if you ever find yourself writing a loop to check a variable and find out if it has changed state, then something has gone wrong somewhere. Instead there should be a way to supply a function that will be called when the state changes.

Update 3

I see elsewhere in comments you mention that the actual problem is caching results, so all my work explaining this was a waste of time. This is the kind of thing you should put in the question.

Update 4

More recently I've written a short blog post on the subject of caching asynchronous call results in JavaScript.

(end of update 4)

Another way to share results is to provide a way for one callback to "broadcast" or "publish" to several subscribers:

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);
            }
        }
    };
}

So:

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("[email protected]", "finished", msg);
});

Then let some slow operation publish its results:

lookupTaxRecords("Homer J. Simpson", finished.publish);

When that one call finishes, it will now call all three subscribers.

○愚か者の日 2024-09-03 02:00:13

对于“准备好后给我打电话”问题的最终答案是回调。回调基本上是分配给对象属性的函数(例如“onload”)。当对象状态改变时,调用该函数。例如,此函数向给定的 url 发出 ajax 请求,并在完成后发出尖叫声:

function ajax(url) {
    var req = new XMLHttpRequest();  
    req.open('GET', url, true);  
    req.onreadystatechange = function (aEvt) {  
        if(req.readyState == 4)
            alert("Ready!")
    }
    req.send(null);  
}

当然,这不够灵活,因为我们可能希望针对不同的 ajax 调用执行不同的操作。幸运的是,javascript 是一种函数式语言,因此我们可以简单地将所需的操作作为参数传递:

function ajax(url, action) {
    var req = new XMLHttpRequest();  
    req.open('GET', url, true);  
    req.onreadystatechange = function (aEvt) {  
        if(req.readyState == 4)
            action(req.responseText);
    }
    req.send(null);  
}

第二个函数可以像这样使用:

 ajax("http://...", function(text) {
      do something with ajax response  
 });

根据注释,这里是一个如何在对象内使用 ajax 的示例

function someObj() 
{
    this.someVar = 1234;

    this.ajaxCall = function(url) {
        var req = new XMLHttpRequest();  
        req.open('GET', url, true);  

        var me = this; // <-- "close" this

        req.onreadystatechange = function () {  
            if(req.readyState == 4) {
                // save data...
                me.data = req.responseText;     
                // ...and/or process it right away
                me.process(req.responseText);   

            }
        }
        req.send(null);  
    }

    this.process = function(data) {
        alert(this.someVar); // we didn't lost the context
        alert(data);         // and we've got data!
    }
}


o = new someObj;
o.ajaxCall("http://....");

这个想法是“关闭” (别名)“this”在事件处理程序中,以便可以进一步传递。

The definitive answer to this "call me when you're ready" problem is a callback. A callback is basically a function that you assign to an object property (like "onload"). When object state changes, the function is called. For example, this function makes an ajax request to the given url and screams when it's complete:

function ajax(url) {
    var req = new XMLHttpRequest();  
    req.open('GET', url, true);  
    req.onreadystatechange = function (aEvt) {  
        if(req.readyState == 4)
            alert("Ready!")
    }
    req.send(null);  
}

Of course, this is not flexible enough, because we presumably want different actions for different ajax calls. Fortunately, javascript is a functional language, so we can simply pass the required action as a parameter:

function ajax(url, action) {
    var req = new XMLHttpRequest();  
    req.open('GET', url, true);  
    req.onreadystatechange = function (aEvt) {  
        if(req.readyState == 4)
            action(req.responseText);
    }
    req.send(null);  
}

This second function can be used like this:

 ajax("http://...", function(text) {
      do something with ajax response  
 });

As per comments, here an example how to use ajax within an object

function someObj() 
{
    this.someVar = 1234;

    this.ajaxCall = function(url) {
        var req = new XMLHttpRequest();  
        req.open('GET', url, true);  

        var me = this; // <-- "close" this

        req.onreadystatechange = function () {  
            if(req.readyState == 4) {
                // save data...
                me.data = req.responseText;     
                // ...and/or process it right away
                me.process(req.responseText);   

            }
        }
        req.send(null);  
    }

    this.process = function(data) {
        alert(this.someVar); // we didn't lost the context
        alert(data);         // and we've got data!
    }
}


o = new someObj;
o.ajaxCall("http://....");

The idea is to "close" (aliased) "this" in the event handler, so that it can be passed further.

北方的韩爷 2024-09-03 02:00:13

欢迎来到SO!顺便说一句,你给人的印象是一个十足的白痴,你的问题完全不清楚:)

这是建立在@Daniel关于使用延续的答案的基础上的。这是一个将多个方法链接在一起的简单函数。很像管道 | 在 unix 中的工作方式。它采用一组函数作为参数,这些函数将按顺序执行。每个函数调用的返回值都会作为参数传递给下一个函数。

function Chain() {
    var functions = arguments;

    return function(seed) {
        var result = seed;

        for(var i = 0; i < functions.length; i++) {
            result = functions[i](result);
        }

        return result;
    }
}

要使用它,请从 Chained 创建一个对象,并将所有函数作为参数传递。您可以在 fiddle 上测试的示例是:

​var chained = new Chain(
    function(a) { return a + " wo"; },
    function(a) { return a + "r"; },
    function(a) { return a + "ld!"; }
);

alert(chained('hello')); // hello world!

要将其与 AJAX 请求一起使用,请将链式函数传递为XMLHttpRequest 的成功回调。

​var callback = new Chain(
    function(response) { /* do something with ajax response */ },
    function(data) { /* do something with filtered ajax data */ }
);

var req = new XMLHttpRequest();  
req.open('GET', url, true);  
req.onreadystatechange = function (aEvt) {  
    if(req.readyState == 4)
        callback(req.responseText);
}
req.send(null);  

重要的是每个函数都依赖于前一个函数的输出,因此您必须在每个阶段返回一些值。


这只是一个建议 - 负责检查数据是否在本地可用或必须发出 HTTP 请求将会增加系统的复杂性。相反,您可以拥有一个不透明的请求管理器,就像您拥有的 metaFunction 一样,并让它决定是在本地还是远程提供数据。

这是一个 示例 Request 对象,它可以在没有任何其他对象或函数的情况下处理这种情况了解数据的来源:

var Request = {
    cache: {},

    get: function(url, callback) {
        // serve from cache, if available
        if(this.cache[url]) {
            console.log('Cache');
            callback(this.cache[url]);
            return;
        }
        // make http request
        var request = new XMLHttpRequest();
        request.open('GET', url, true);
        var self = this;
        request.onreadystatechange = function(event) {
            if(request.readyState == 4) {
                self.cache[url] = request.responseText;
                console.log('HTTP');
                callback(request.responseText);
            }
        };
        request.send(null);
    }
};

要使用它,您需要调用 Request.get(..),它会返回缓存数据(如果可用),否则会进行 AJAX 调用。如果您正在寻找对缓存的精细控制,则可以传递第三个参数来控制数据应缓存多长时间。

Request.get('<url>', function(response) { .. }); // HTTP
// assuming the first call has returned by now
Request.get('<url>', function(response) { .. }); // Cache
Request.get('<url>', function(response) { .. }); // Cache

Welcome to SO! Btw, You come across as a total nitwit and your question is totally unclear :)

This is building upon @Daniel's answer of using continuations. It is a simple function that chains multiple methods together. Much like how the pipe | works in unix. It takes a set of functions as its arguments which are to be executed sequentially. The return value of each function call is passed on to the next function as a parameter.

function Chain() {
    var functions = arguments;

    return function(seed) {
        var result = seed;

        for(var i = 0; i < functions.length; i++) {
            result = functions[i](result);
        }

        return result;
    }
}

To use it, create an object from Chained passing all functions as parameters. An example you can test on fiddle would be:

​var chained = new Chain(
    function(a) { return a + " wo"; },
    function(a) { return a + "r"; },
    function(a) { return a + "ld!"; }
);

alert(chained('hello')); // hello world!

​To use it with an AJAX request, pass the chained function as the success callback to the XMLHttpRequest.

​var callback = new Chain(
    function(response) { /* do something with ajax response */ },
    function(data) { /* do something with filtered ajax data */ }
);

var req = new XMLHttpRequest();  
req.open('GET', url, true);  
req.onreadystatechange = function (aEvt) {  
    if(req.readyState == 4)
        callback(req.responseText);
}
req.send(null);  

The important thing is that each function depends on the output of the previous function, so you must return some value at each stage.


This is just a suggestion - giving the responsibility of checking whether data is available locally or an HTTP request must be made is going to increase the complexity of the system. Instead, you could have an opaque request manager, much like the metaFunction you have, and let it decide if the data is to be served locally or remotely.

Here is a sample Request object that handles this situation without any other objects or functions knowing where the data was served from:

var Request = {
    cache: {},

    get: function(url, callback) {
        // serve from cache, if available
        if(this.cache[url]) {
            console.log('Cache');
            callback(this.cache[url]);
            return;
        }
        // make http request
        var request = new XMLHttpRequest();
        request.open('GET', url, true);
        var self = this;
        request.onreadystatechange = function(event) {
            if(request.readyState == 4) {
                self.cache[url] = request.responseText;
                console.log('HTTP');
                callback(request.responseText);
            }
        };
        request.send(null);
    }
};

To use it, you would make a call to Request.get(..), and it returns cached data if available or makes an AJAX call otherwise. A third parameter could be passed to control how long the data should be cached for, if you're looking for granular control over caching.

Request.get('<url>', function(response) { .. }); // HTTP
// assuming the first call has returned by now
Request.get('<url>', function(response) { .. }); // Cache
Request.get('<url>', function(response) { .. }); // Cache
瞄了个咪的 2024-09-03 02:00:13

我已经解决了,现在看起来效果很好。稍后我整理完之后会发布我的代码。同时,非常感谢您的帮助!

更新

尝试了 Webkit(Safari、Chrome)、Mozilla 和 Opera 中的代码。似乎工作得很好。期待任何答复。

更新 2

我更改了 tieExc() 方法以集成 Anurag 的链式函数调用语法。现在,您可以通过将函数作为参数传递来在完成检查时调用任意数量的函数。

如果您不想阅读代码,请尝试:http://jsfiddle.net/UMuj3/ (顺便说一句,JSFiddle 是一个非常简洁的网站!)。

JS 代码:

/**/function meta() {

var myMeta = this ;

/**  **/this.cllFnc = function(tgt,lgt) { //!! first function

    this.trigger = 0 ;  //!! status flag, initially zero
    var that = this ;   //!! required to access parent scope from inside nested function

    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() { //!! simulates longer AJAX call, duration 5000ms

        that.trigger = 1 ;  //!! status flag, one upon completion

    },5000) ;

} ;

/**  **/this.rcvFnc = function(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!') ;

} ;

/**  **/this.callCheck = function(obj) {    

    return (obj.trigger == 1)   ?   true
        :   false
        ;

} ;

/**  **/this.tieExc = function() {

    var functions = arguments ;

    var myItv = window.setInterval(function() {

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

        var myCallCheck = myMeta.callCheck(functions[0]) ; //!! checks property "trigger"

        if(myCallCheck == true) { 

            clearInterval(myItv) ;

            for(var n=1; n < functions.length; n++) {

                functions[n].call() ;

            }

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

    },100) ;



} ;

}​

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>

      Javascript Phased Execution Test Page

    </title>

</head>

<body>

        <div id='target' style='float:left ; height:7.5em ; width:10em ; padding:0.5em 0 0 0; font-size:4em ; 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="tieCalls()" onmousedown="var myMeta = new meta() ; var myCll = new myMeta.cllFnc('target') ; new myMeta.tieExc(myCll, function() { myMeta.rcvFnc('target') ; }, function() { alert('this is fun stuff!') ; } ) ;" /><br />
        </div>

<body>


</html>

I've worked it out and it seems to work perfectly well now. I will post my code later after I have sorted it out. In the meantime, thanks a lot for you assistance!

Update

Tried the code in Webkit (Safari, Chrome), Mozilla and Opera. Seems to work just fine. Looking forward to any replies.

Update 2

I changed the tieExc() method to integrate Anurag's chained function call syntax. Now you can call as many functions as you want upon completion check by passing them as arguments.

If you are not inclined to read the code, try it: http://jsfiddle.net/UMuj3/ (btw, JSFiddle is a really neat site!).

JS-Code:

/**/function meta() {

var myMeta = this ;

/**  **/this.cllFnc = function(tgt,lgt) { //!! first function

    this.trigger = 0 ;  //!! status flag, initially zero
    var that = this ;   //!! required to access parent scope from inside nested function

    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() { //!! simulates longer AJAX call, duration 5000ms

        that.trigger = 1 ;  //!! status flag, one upon completion

    },5000) ;

} ;

/**  **/this.rcvFnc = function(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!') ;

} ;

/**  **/this.callCheck = function(obj) {    

    return (obj.trigger == 1)   ?   true
        :   false
        ;

} ;

/**  **/this.tieExc = function() {

    var functions = arguments ;

    var myItv = window.setInterval(function() {

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

        var myCallCheck = myMeta.callCheck(functions[0]) ; //!! checks property "trigger"

        if(myCallCheck == true) { 

            clearInterval(myItv) ;

            for(var n=1; n < functions.length; n++) {

                functions[n].call() ;

            }

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

    },100) ;



} ;

}​

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>

      Javascript Phased Execution Test Page

    </title>

</head>

<body>

        <div id='target' style='float:left ; height:7.5em ; width:10em ; padding:0.5em 0 0 0; font-size:4em ; 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="tieCalls()" onmousedown="var myMeta = new meta() ; var myCll = new myMeta.cllFnc('target') ; new myMeta.tieExc(myCll, function() { myMeta.rcvFnc('target') ; }, function() { alert('this is fun stuff!') ; } ) ;" /><br />
        </div>

<body>


</html>
爱冒险 2024-09-03 02:00:13

一个非常简单的解决方案是使您的第一个 ajax 调用同步。它是可选参数之一。

A very simple solution would be to make your first ajax call synchronous. It's one of the optional parameters.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文