2016-02-13 3 views
5

Ich glaube, ich habe es endlich geschafft, mich in den meisten Fällen um Javascript/ES6 Promises zu kümmern. Es war nicht einfach! Aber da ist etwas, das mich am Design verblüfft.Ist Javascript Promise API mehr verschachtelt als es sein muss?

Warum nimmt der Promise-Konstruktor einen Rückruf an? Angesichts der Tatsache, dass der Callback sofort aufgerufen wird, kann der Anrufer diesen Code nicht einfach nur ausführen, wodurch eine unnötige Ebene des Umstands vermieden wird: "Ruf mich nicht an, ich ruf dich an".

Hier ist, was ich denke, als das prototypische Beispiel für die Verwendung von Promise, kopiert von Jake Archibald Javascript Tutorials Tutorial http://www.html5rocks.com/en/tutorials/es6/promises/#toc-promisifying-xmlhttprequest, mit Kommentaren gestrippt.

Es ist ein-Promise basierte Wrapper für eine XMLHttpRequest-GET-Anfrage:

function get(url) { 
    return new Promise(function(resolve, reject) { 
    var req = new XMLHttpRequest(); 
    req.open('GET', url); 
    req.onload = function() { 
     if (req.status == 200) { 
     resolve(req.response); 
     } 
     else { 
     reject(Error(req.statusText)); 
     } 
    }; 
    req.onerror = function() { 
     reject(Error("Network Error")); 
    }; 
    req.send(); 
    }); 
} 

Für mich ist der obige Code wäre viel einfacher zu verstehen, wenn sie wie folgt neu geschrieben wurden, eine sehr leicht andere Art von Versprechen mit no-arg Konstruktor, dass ich mich vor, eine mit und lösen/abweisen Methoden:

function get(url) { 
    var promise = new MyEasierToUnderstandPromise(); 
    var req = new XMLHttpRequest(); 
    req.open('GET', url); 
    req.onload = function() { 
    if (req.status == 200) { 
     promise.resolve(req.response); 
    } 
    else { 
     promise.reject(Error(req.statusText)); 
    } 
    }; 
    req.onerror = function() { 
    promise.reject(Error("Network Error")); 
    }; 
    req.send(); 
    return promise; 
} 

MyEasierToUnderstandPromise nicht zu schwer ist, in Bezug auf den Versprechen umzusetzen. Zuerst versuchte ich, es zu einer tatsächlichen Unterklasse von Promise zu machen, aber aus irgendeinem Grund konnte ich das nicht zur Arbeit bringen; so stattdessen implementiert ich es als eine einfache Fabrik-Funktion, das ein einfaches alten Versprechen Objekt mit einem paar zusätzlichen Funktionen zurückzugibt angebracht, dass wie Member-Funktionen verhalten:

function NewMyEasierToUnderstandPromise() { 
    var resolveVar; 
    var rejectVar; 
    var promise = new Promise(function(resolveParam, rejectParam) { 
    resolveVar = resolveParam; 
    rejectVar = rejectParam; 
    }); 
    promise.resolve = resolveVar; 
    promise.reject = rejectVar; 
    return promise; 
}; 

Also, warum nicht so ausgelegt ist, Versprechen? Ich denke, wenn es so wäre, hätte es mir geholfen, Promises viel schneller zu verstehen - ich wette, es hätte meine Lernzeit halbiert.

Ich weiß, dass viele kluge Leute die Promise-API gemacht haben, und jeder scheint im Allgemeinen glücklich und stolz darauf zu sein, also frage ich mich, was sie gedacht haben.

+1

Ihre "easierToUnderstandPromise" ist wie jQuery.Deferred in einer Art und Weise. Mit Ihrem Entwurf stellt das zurückgegebene Versprechen notwendigerweise Lösungs-/Zurückweisungsmethoden bereit. Ich habe irgendwo gelesen, warum das eine "schlechte Sache" ist, aber ich kann diese Ressource nicht finden (es ist Jahre her, seit ich sie gelesen habe) –

+0

Lesen Sie über [ES7 async/await] (https://jakearchibald.com/2014/es7-async-Funktionen /). –

+0

Es gibt auch die verzögerte Muster, aber [es ist aus gutem Grund veraltet] (http://stackoverflow.com/q/28687566/1048572) – Bergi

Antwort

6

Ihre Version ist nicht ausnahmesicher, wohingegen Promises/A + sicher sind, da sie vom Promise Konstruktor abgefangen werden.

+0

Große Antwort, danke. Ich denke, dass alle einführenden Dokumente und Tutorials durch das Erwähnen dieses Themas stark verbessert werden würden - mein Gehirn möchte einfach keine Dinge absorbieren, für die es keinen Grund sehen kann! –

1

Als ancilliary Informationen, wenn ein Skript wie eine ES6 Versprechen Kette definiert

var promise = new Promise(executor).then(something).catch(handleError); 

das Versprechen variable gelassen wird gesetzt durch den .catch Methodenaufruf an das Versprechen zurückgegeben. Es wird tatsächlich verhindert, dass die gesamte Kette oder die Promise-Objekte durch die von der Executor-Funktion gehaltenen Auflösungs-/Zurückweisungsfunktionsreferenzen als Müll gesammelt werden. Wenn die Auflösung aufgerufen wird (in der Regel asynchron, nachdem der Executor zurückkehrt, bevor die Auflösungs-/Ablehnungsfunktionen den Gültigkeitsbereich verlassen), wird then listener "something" aufgerufen, mit einem internen Executor der Promise-Plattform, der Referenzen für die von der then Anruf, um es zu verhindern und alle folgenden angeketteten Versprechungen von sich selbst werden vorzeitig Müll gesammelt.

Unter dem vorgeschlagenen verzögerten Modell können Sie die Versprechenskette nicht auf diese Weise einrichten, weil Sie eine Referenz auf die erste Versprechen in der Kette benötigen, um es zu lösen oder abzulehnen.Der Code wird mehr wie

var promise = new Promise(); // no executor 
promise.then(something).catch(handleError); 
initiateOperation(promise); 

und dann asynchron in Betrieb Antwortcode

promise.resolve(value); // if no error occurred 
promise = null;   // explicit discard of first promise reference to allow GC? 

Der allgemeine minimilistic Ansatz von ES6 jetzt verspricht beginnt (autsch) zu sehen vielversprechend aus. Ich habe Verständnis dafür, wie schwierig es ist zu lernen, wie Versprechen funktionieren - eine interessante Reise !!!

+1

Nein, Sie müssen Verweise auf "Versprechen" im asynchronen Callback nicht explizit verwerfen, genauso wie Sie Verweise auf "resolve"/"reject" im Executor-Bereich nicht explizit verwerfen müssen. – Bergi

5

Versprechen sollen als Werte verwendet werden. Der ES-Konstruktor-Ansatz kapselt die Erzeugung des Versprechens, das dann wie ein Wert weitergegeben werden kann. Wenn dieser Wert übergeben wird, benötigt der Consumer dieses Werts keine resolve und reject. Diese Funktionen sollten daher nicht Teil der öffentlichen API sein.

(und all das Zeug über die Ausnahmebehandlung und Chaining)