Frage

Dies ist mein Erster post auf stackoverflow, also bitte don ' T flame mich zu hart, wenn ich stoße, wie ein totaler Schwachkopf, oder wenn ich bin nicht in der Lage ot Mach mich vollkommen klar.:-)

Hier ist mein problem:Ich bin versucht zu schreiben, eine javascript-Funktion, die "Bande" zwei Funktionen, zum anderen durch die überprüfung der ersten Fertigstellung und dann die Ausführung der zweiten.

Die einfache Lösung für dies wäre offensichtlich zu schreiben, eine meta-Funktion, ruft Funktionen innerhalb der it-Bereich.Jedoch, wenn die erste Funktion asynchron ist (speziell ein AJAX-Aufruf) und die zweite Funktion ist die erste der Ergebnis-Daten, die einfach nicht funktionieren.

Meine Idee für eine Lösung zu geben, die erste Funktion eine "fahne", d.h.machen Sie es schaffen, eine öffentliche Eigenschaft "dieses.trigger" (initialisiert von "0" auf "1" gesetzt bei Beendigung), wenn es heißt;tun, machen es möglich für eine andere Funktion zu überprüfen, die fahne für seinen Wert ([0,1]).Wenn die Bedingung erfüllt ist ("trigger == 1") die zweite Funktion sollte aufgerufen.

Die folgenden ist ein abstraktes Beispiel-code, den ich zum testen verwendet:

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

Der HTML-Teil für die Prüfung:

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

Ich bin mir ziemlich sicher, dass dies ein Problem mit dem javascript-Bereich, da habe ich überprüft, ob der trigger wird auf "1" gesetzt richtig-und es tut.Sehr wahrscheinlich wird das "checkCall ()" - Funktion nicht erhalten das aktualisierte Objekt, sondern nur prüft Ihre alte Instanz, die offensichtlich nie flags Fertigstellung durch die Einstellung "diesem.- trigger" auf "1".Wenn ja, habe ich nicht wissen, wie zu Adresse die Problem.

Anyway, ich hoffe jemand hat eine Idee oder Erfahrung mit dieser besonderen Art von problem.

Vielen Dank für das Lesen!

FK

War es hilfreich?

Lösung 4

Ich habe es geklappt und jetzt scheint es sehr gut zu arbeiten. Ich werde meinen Code schreiben später, nachdem ich es haben aussortiert. In der Zwischenzeit, vielen Dank für Sie Hilfe!

Aktualisieren

Versucht, den Code in Webkit (Safari, Chrome), Mozilla und Opera. Scheint ganz gut zu funktionieren. Wir freuen uns auf alle Antworten.

Update 2

änderte ich die tieExc () -Methode Anurag der verkettete Funktionsaufruf Syntax zu integrieren. Jetzt können Sie so viele Funktionen aufrufen, wie Sie nach Abschluss überprüfen möchten, indem sie als Argumente zu übergeben.

Wenn Sie nicht geneigt sind, den Code zu lesen, versuchen Sie es: http://jsfiddle.net/UMuj3/ (btw, JSFiddle ist eine wirklich nette Seite!).

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>

Andere Tipps

Können Sie Sie nutzen ein feature von JS aufgerufen Schließung.Kombinieren Sie das mit einem sehr verbreitet JS Muster als "continuation passing style" und Sie haben Ihre Lösung.(Keines dieser Dinge original JS, aber Massiv 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
}

Die "continuation-passing-Stil" bezieht sich auf den Rückruf.Anstatt einen Wert, der für jede Funktion ruft eine callback - (Fortsetzung) - und gibt es die Ergebnisse.

Sie können dann binden Sie die beiden zusammen einfach:

foo(input1, function(results1) {

    bar(results1, function(results2) {

        alert(results2);
    });
});

Die verschachtelte anonyme Funktionen "sehen" können Variablen aus dem Geltungsbereich der Sie Leben.So gibt es keine Notwendigkeit zu verwenden spezielle Eigenschaften, um Informationen rund um.

Update

Um zu klären, die in Ihrer Frage ist ein code-snippet, es ist klar, dass Sie denken ungefähr so:

Ich habe einen lang Laufenden asynchronen Betrieb, also muss ich wissen, wenn es Oberflächen, damit die nächste Betrieb.So muss ich machen, dass Zustand sichtbar als Eigentum.Dann anderswo kann ich in einer Schleife ausführen, immer wieder untersuchen, die Eigenschaft zu sehen Sie, wenn es änderungen an der "abgeschlossen" Zustand, so dass ich wissen, wenn, um fortzufahren.

(Und dann als ein problematischer Faktor, der Schleife zu verwenden hat setInterval mit dem laufen beginnen und clearInterval um zu beenden, damit andere JS-code ausführen, aber es ist im Grunde ein "polling-Schleife" trotzdem).

Sie brauchen nicht zu tun!

Anstelle von Ihrer ersten Funktion festlegen einer Eigenschaft auf Fertigstellung, machen es eine Funktion aufrufen.

Um dies absolut klar, lassen Sie uns gestalten Sie Ihre 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, gibt es einen Fehler gibt.Kopieren Sie den aktuellen Wert der trigger Eigenschaft in eine neue lokale variable mit dem Namen trigger.Am Ende dann ordnen Sie 1, um das lokale variable.Niemand anderes wird in der Lage sein zu sehen, dass.Lokale Variablen sind privat, um die Funktion. Aber Sie brauchen nicht zu tun dies trotzdem, so Lesen Sie weiter...]

Alles, was wir tun müssen ist, sagen Sie, die Funktion zu nennen, was, wenn es fertig ist, und loszuwerden, die Eigentums-Einstellung:

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

Es gibt jetzt keine Notwendigkeit, für Ihre "Ruf-check" oder, Ihren besonderen tieExc Helfer.Sie können leicht zu Band zwei Funktionen zusammen mit sehr wenig code.

var mySpan = "#myspan";

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

Ein weiterer Vorteil dieser ist, dass wir passieren können verschiedene Parameter, um die zweite Funktion.Mit Ihrem Ansatz, die gleichen Parameter übergeben werden beide.

Für Beispiel, die erste Funktion könnte ein paar Anrufe, um eine AJAX-Dienst (unter Verwendung von jQuery für die Kürze):

function getCustomerBillAmount(name, callback) {

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

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

    });
}

Hier, callback akzeptiert der Kunde die Rechnung Betrag, und das AJAX get Aufruf übergibt die empfangenen Wert an die Funktion übergeben wir es, so die callback schon kompatibel ist, und kann so direkt handeln, wie der Rückruf für den zweiten AJAX-Aufruf.So ist dies selbst ein Beispiel für das binden zwei asynchrone Aufrufe in der Folge zusammen und wickelte Sie in dem, was erscheint (von außen) eine asynchrone Funktion.

Dann können wir diese Kette mit einem anderen Betrieb:

function displayBillAmount(amount) {

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

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

Oder wir könnten (wieder) benutzt haben, eine anonyme Funktion:

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

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

So durch die Verkettung von Funktionsaufrufen wie diese, jeder Schritt kann die übergabe von Informationen zum nächsten Schritt, sobald es verfügbar ist.

Indem Sie Funktionen ausführen, die einen Rückruf, wenn Sie fertig sind, Sie sind befreit von jeglichen Einschränkungen, wie die einzelnen Funktionen arbeiten intern.Es kann AJAX-Aufrufe, Timer, was auch immer.Solange die "Fortsetzung" callback übergeben wird vorwärts, es kann eine beliebige Anzahl der Schichten, die asynchron arbeiten.

Im Grunde, in einem asynchronen system, wenn Sie sich jemals schreiben einer Schleife, um zu überprüfen, die eine variable und finden Sie heraus, ob es seinen Zustand geändert hat, dann ist etwas schief gegangen ist, irgendwo.Stattdessen sollte es eine Möglichkeit geben, eine Funktion, die aufgerufen wird, wenn der Status ändert.

Update 3

Ich sehe an anderer Stelle in die Kommentare, die Sie erwähnen, dass das eigentliche problem ist das caching der Ergebnisse, so dass alle meine Arbeit zu erklären, das war eine Verschwendung von Zeit.Dies ist die Art von Sache, die Sie sollten in der Frage.

Update 4

Vor kurzem habe ich geschrieben eine kurze blog-post zum Thema caching, asynchronen Aufrufs Ergebnisse in JavaScript.

(Ende update 4)

Ein anderer Weg, um Ergebnisse zu teilen ist eine Möglichkeit zu geben, einen callback zu "broadcast" oder "veröffentlichen", um mehrere Abonnenten:

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

Also:

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

Dann lassen Sie einige langsame Vorgang veröffentlichen Ihre Ergebnisse:

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

Wenn Sie, dass ein Anruf beendet ist, es wird rufen Sie nun alle drei Abonnenten.

Die endgültige Antwort auf diese Frage „rufen Sie mich an, wenn Sie bereit sind,“ Problem ist es, ein Rückruf . Ein Rückruf ist im Grunde eine Funktion, die Sie auf eine Objekt-Eigenschaft (wie „onload“) zuordnen. Wenn Objektzustand ändert, wird die Funktion aufgerufen. Zum Beispiel macht diese Funktion einen Ajax-Request an die angegebene URL und schreit, wenn es abgeschlossen ist:

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

Natürlich ist dies nicht flexibel genug, weil wir vermutlich verschiedene Aktionen für unterschiedliche Ajax-Aufrufe wollen. Glücklicherweise Javascript ist eine funktionale Sprache, so dass wir einfach die gewünschte Aktion als Parameter übergeben können:

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

Diese zweite Funktion kann wie folgt verwendet werden:

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

Wie pro Kommentar, hier ein Beispiel, wie innerhalb eines Objekts verwenden 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://....");

Die Idee ist, "close" (aliased) "this" in den Event-Handler, so dass es weiter geführt werden kann.

Willkommen bei SO! Btw kommen, Sie über als Gesamt nitwit und Ihre Frage ist völlig unklar:)

Dies baut auf @ Daniel Antwort von Fortsetzungen verwenden. Es ist eine einfache Funktion, dass Ketten mehr Methoden zusammen. Ähnlich wie wie das Rohr | arbeitet in Unix. Es nimmt eine Reihe von Funktionen als Argumente, die nacheinander ausgeführt werden sollen. Der Rückgabewert von jedem Funktionsaufruf wird an die nächste Funktion als Parameter übergeben.

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

Um es zu verwenden, erstellen Sie ein Objekt aus Chained alle Funktionen als Parameter übergeben. Ein Beispiel können Sie Test auf Geige wäre:

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

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

Um es mit einem AJAX-Request zu verwenden, übergeben Sie die verkettete Funktion als Erfolg Rückruf an den 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);  

Das Wichtigste ist, dass jede Funktion auf der Ausgabe der vorherigen Funktion abhängig ist, so dass Sie einen Wert in jeder Phase zurückkehren.


Dies ist nur ein Vorschlag - gibt die Verantwortung für die Überprüfung, ob die Daten lokal verfügbar sind oder eine HTTP-Anforderung muss gemacht werden, wird die Komplexität des Systems zu erhöhen. Stattdessen könnten Sie einen undurchsichtigen Anforderungs-Manager haben, ähnlich wie die metaFunction Sie haben, und lassen Sie sich entscheiden, ob die Daten lokal bedient werden soll oder aus der Ferne.

Hier ist ein Probe Request Objekt dass Griffe diese Situation ohne andere Objekte oder Funktionen zu wissen, wo die Daten serviert wurde von:

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

Um es zu nutzen, würden Sie einen Anruf zu Request.get(..) machen, und es gibt im Cache gespeicherten Daten, falls verfügbar oder macht einen AJAX-Aufruf anders. Ein dritte Parameter zur Steuerung weitergegeben werden kann, wie lange die Daten für zwischengespeichert werden, wenn Sie für granulare Kontrolle über Caching suchen.

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

Eine sehr einfache Lösung wäre Ihr erster Ajax-Aufruf synchron zu machen. Es ist eines der optionalen Parameter.

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top