2015-01-28 9 views
6

Viele Male, brauchte ich eine solche faulen asynchrone Laden in Javascript zu schreiben:Wie vermeidet man dieses Async-Lazy-Pattern?

if (myvar != undefined){ 
    doSomeTreatment(myvar) 
} else { 
    loadMyVarAsynchronously().then(function(value){ 
     myvar = value 
     doSomeTreatment(myvar) 
    }) 
} 

Hier myvar einige Attribut eines Hash sein würde, nicht eine lokale Variable. loadMyVarAsynchronously lädt asynchron der Wert für myvar (mit zum Beispiel ein Promise oder ein JQuery Deferred)

Gibt es ein Muster in diesem Code zweimal die folgende Zeile zu schreiben, um zu vermeiden?

doSomeTreatment(myvar) 
+0

oops - sind diese jQuery verspricht oder eine neue „Standard“ Versprechen? – Alnitak

+0

Meine Frage soll agnostisch sein, damit die Antwort die beste Lösung vorschlagen kann! Verwenden Sie einfach den besten Standard oder nicht Promise oder Deferred oder irgendetwas anderes, das Sie mögen. –

Antwort

1

Es ist wirklich schwer zu Grunde zu wissen, was Sie hier ohne konkretes, reale Welt Beispiel tun. Dies ist definitiv kein Muster, das ich sehr oft in meinem Code verwende, daher habe ich den Verdacht, dass Sie es beheben könnten, indem Sie Ihre Annahmen neu bewerten.

Das gesagt, Sie können Versprechen Verkettung verwenden. Beachten Sie in diesem Fall, dass "promiseThatLoadsVar" ein Versprechen ist, es ist keine Funktion, die ein Versprechen zurückgibt. Das bedeutet, dass es einen Zustand beibehält, und das zweite Mal, wenn dieser Code weiterläuft, wird er sofort zurückkehren.

Wenn Sie eine klarere Frage stellen könnten, würde ich gerne eine klarere Antwort für Sie bereitstellen.

Bearbeiten: Ich möchte auf das in den Kommentaren gestellte Beispiel näher eingehen. Hier ist eine Lösung, die die Bibliothek LoDash verwendet. (BTW, sollten Sie lodash oder Unterstreichung verwenden, sie sind im Grunde die Standardbibliothek von Javascript).

var getData = _.once(function() { return $.getJSON(...) }); 

$('button').on('click', function() { 
    getData().then(function(data) { showDialog(data) }) 
}) 

Hinweis hier, dass getData eine gewickelte Funktion, dass nach dem ersten Aufruf wird nur die gleiche Sache immer und immer wieder zurückkehren, ohne die Funktion erneut aufrufe. Wenn Sie es das erste Mal aufrufen, wird ein Versprechen zurückgegeben, das beim Abrufen der Daten aufgelöst wird. Das zweite Mal erhalten Sie wieder das gleiche Versprechen, die wahrscheinlich bereits gelöst werden.

Möchten Sie Parameter an getData übergeben?

var getData = _.memoize(function(id) { return $.getJSON(url+id) }); 

$('button').on('click', function() { 
    getData($('#selector').val()).then(function(data) { showDialog(data) }) 
}) 

Dies wird das Gleiche tun, aber die Versprechen der ersten Eingangsparameter Cache getData geben.

+1

Ich bin wirklich überrascht, dass Sie mir sagen, dass dies kein nützliches Muster ist. Dies ist eine sehr klassische Art des Lazy-Ladens, mit Bonus der asynchrone Parameter. Dies ist, IMHO, eine ziemlich gute Optimierung hinsichtlich der Leistung und der GUI-Reaktivität. –

+1

@ RémiDoolaeghe Ich sage nicht, dass es kein nützliches Muster ist, aber die Art und Weise, wie ich die UI-Architektur entwerfe und die Bibliotheken, die ich benutze (ich bin ein großer Fan von Knockout und ReactJs), tun das in der Regel so nicht notwendig. Ich lade natürlich nicht alle Daten im Voraus, aber es ist extrem selten, dass ich Wächter wie Ihre & ldquor; undefinierte "Prüfung manuell verwalten muss, und noch seltener, dass ich eine Variable von einem höheren Bereich in einem Rückruf modifizieren werde. Ich denke, es gibt einige Änderungen an der Architektur, die Sie vornehmen können, um das Problem vollständig zu vermeiden. –

+0

Ich muss darüber nachdenken. Was du sagst, kann einen Sinn ergeben. Ich muss sehen, ob ich Legacy-Code an diese Vision anpassen kann. Danke, dass Sie Ihre Idee präzisiert haben. –

2

Wenn myvar bereits definiert ist, dass in $.when() passieren können und haben es automatisch in ein aufgelöst Versprechen eingewickelt:

var def = (myVar !== undefined) ? myVar : loadMyVarAsynchronously(); 

$.when(def).then(doSomeTreatment); 

Sprechen Sie die Zuordnung zu myVar innerhalb doSomeTreatment(), falls erforderlich, statt innerhalb der anonym .then Rückruf, damit können Sie doSomeTreatment direkt als Funktionsreferenz übergeben.

BEARBEITEN oops, wollten Sie Standardversprechen oder jQuery Versprechungen? Das oben genannte ist jQuery Stil.

+1

jQuery verspricht, dass ich weine. Wie könnte das Team [sie so sehr falsch machen] (https://blog.domenic.me/youre-missing-the-point-of-promises/)? – Sukima

+0

@Sukima, weil sie die API standardisierten, bevor A + eine Sache war, glaube ich. –

+0

@GeorgeMauer Ich bin mir nicht sicher, dass es vorher war, aber ich glaube, dass die Einführung einer Promise-_like_ API durch jQuery einen großen Beitrag zur Förderung des Konzepts geleistet hat. – Alnitak

1

Ja, Sie sollten ein Versprechen für den Wert erstellen. Dann brauchen Sie nur nur diese Funktion befestigen einmal als Handler:

var loadedMyVar = myvar!=undefined ? Promise.resolve(myvar) : loadMyVarAsynchronously(); 
loadedMyVar.then(doSomeTreatment); 

Oh, und Sie sollten nicht asynchron myvar neu zuweisen müssen, verwenden Sie einfach die loadedMyVar Versprechen überall. Vielleicht verwendet sogar

var loadedMyVar = loadedMyVar || loadMyVarAsynchronously(); 
loadedMyVar.then(function(value) { 
    return myvar = value; 
}).then(doSomeTreatment); 
+0

Ich muss myvar in meinem Fall neu zuweisen, weil ich zwei Möglichkeiten benutze, um mit ractive zu binden, also werde ich in der asynchronen Ladefunktion neu zuweisen –

+0

Meh ok, vergewissere dich, dass du dich nicht auf diesen Wert verlässt, um ihn anzurufen 'loadMyVarAsynchronicly()', besser direkt nach dem Versprechen suchen – Bergi

+0

Das macht Sinn. Danke für diese Warnung. –

1

Es gibt ein Problem mit der Verwendung von myvar != undefined als Ihre Überprüfung, ob es abgerufen werden soll, und das ist, dass möglicherweise bereits ein Abrufvorgang ausgeführt wird, wenn Sie einen anderen initiieren. Sie würden die Anfrage zweimal (oder öfter!) Ausführen.

Um dies zu vermeiden, können Sie auf das Versprechen halten kann, sie als zukünftigen Wert für myvar Behandlung und myvar nicht verwenden:

// assume myvarpromise is persisted somewhere longer than the 
// life of this function 
myvarpromise = myvarpromise || loadMyVarAsynchronously(); 
myvarpromise.then(doSomeTreatment); 
+0

Ich hatte einen Zweifel daran: Betrachten Sie ein Versprechen, das bereits gelöst wurde. Rückruf (mit einem Anruf an dann (...))) wird es sofort anrufen. –

+0

@ RémiDoolaeghe Wenn '.then()' auf einem Versprechen aufgerufen wird, das bereits aufgelöst wurde, wird der Handler asynchron auf Promises/A + -kompatible Versprechungen und sofort auf jQuery-Versprechungen und einigen anderen Arten von Versprechungen aufgerufen. – JLRishe

Verwandte Themen