2016-02-13 12 views
5

Penso di essere finalmente riuscito a piegare la mente alle promesse di javascript/ES6, per la maggior parte. Non è stato facile! Ma c'è qualcosa che mi sconcerta del design.L'API di javascript Promise è più complicata di quanto dovrebbe essere?

Perché il costruttore Promise effettua una richiamata? Dato che il callback è chiamato immediatamente, non è possibile che il chiamante esegua quel codice, evitando così un livello non necessario di piegamento mentale "non chiamarmi, ti chiamo"?

Ecco quello che penso come l'esempio prototipo di utilizzo Promise, copiato dal tutorial di Jake Archibald Javascript Promises http://www.html5rocks.com/en/tutorials/es6/promises/#toc-promisifying-xmlhttprequest, con i commenti spogliati.

Si tratta di un wrapper promessa-based per una richiesta di XMLHttpRequest GET:

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

Per me, il codice di cui sopra sarebbe molto più facile da capire se fosse riscritta come segue, con un po 'diverso tipo di promessa che sto immaginando, avendo un costruttore no-arg e risolvere/rifiutare metodi:

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 non è troppo difficile da attuare in termini di promessa. All'inizio ho provato a farne una vera e propria sottoclasse di Promise, ma per qualche motivo non ho potuto farlo funzionare; così invece ho implementato come una semplice funzione di fabbrica, che restituisce un oggetto semplice promessa vecchia con un paio di funzioni extra attaccato che si comportano come funzioni membro:

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

Quindi, perché non è promessa progettato come questo? Penso che se lo fosse, mi avrebbe aiutato a capire Promises molto più velocemente ... Scommetto che avrebbe ridotto il mio tempo di apprendimento a metà.

So che molte persone intelligenti hanno contribuito all'API Promise, e tutti sembrano essere generalmente felici e orgogliosi di ciò, quindi mi sto chiedendo cosa stessero pensando.

+1

vostro "easierToUnderstandPromise" è come jQuery.Deferred in un certo senso. Con il tuo progetto, la promessa restituita espone necessariamente i metodi di risoluzione/rifiuto. Ho letto da qualche parte perché questa è una "brutta cosa", ma non riesco a trovare quella risorsa (sono passati anni da quando l'ho letta) –

+0

Leggi informazioni su [ES7 async/await] (https://jakearchibald.com/2014/ES7-asincroni funzioni /). –

+0

C'è anche lo schema differito, ma [è deprecato per una buona ragione] (http://stackoverflow.com/q/28687566/1048572) – Bergi

risposta

6

La tua versione non è sicura rispetto alle eccezioni, mentre Promises/A + sono al sicuro poiché vengono catturate dal costruttore Promise.

+0

Ottima risposta, grazie. Penso che tutti i documenti introduttivi e le esercitazioni sarebbero notevolmente migliorate menzionando questo ... il mio cervello non vuole assorbire cose per le quali non può vedere una ragione! –

1

Come accessorie informazioni, quando uno script definisce una catena promessa ES6 come

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

la variabile promessa rimane impostato la promessa restituito dalla chiamata metodo .catch. L'intera catena o gli oggetti Promise vengono in realtà impediti dalla raccolta di dati inutili dai riferimenti alla funzione di risoluzione/rifiuto contenuti nella funzione executor. Se viene chiamata la risoluzione (in genere in modo asincrono dopo il ritorno dell'esecutore ma prima che le funzioni di risoluzione/rifiuto escono dall'ambito), viene chiamato il parametro then "something", con un esecutore interno della piattaforma Promise contenente i riferimenti di funzione di risoluzione/rifiuto per la promessa restituita da la chiamata then per impedirlo e qualsiasi promessa concatenata da parte di se stessi viene raccolta in modo prematuro.

In base al modello posticipato suggerito non è possibile impostare la catena di promessa in questo modo perché è necessario un riferimento alla prima promessa nella catena per risolverla o rifiutarla.Il codice diventa più simile

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

e poi in modo asincrono in codice di risposta funzionamento

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

L'approccio minimilistic generale della ES6 promette ora comincia a guardare promettendo (ahi). Sono solidale con la tua difficoltà nell'imparare come funzionano le promesse - un viaggio interessante !!!

+1

No, non è necessario eliminare esplicitamente i riferimenti a 'promise' nel callback asincrono, proprio come non è necessario eliminare esplicitamente i riferimenti a' resolve'/'reject' nell'ambito executor. – Bergi

5

Le promesse devono essere utilizzate come valori. L'approccio del costruttore ES racchiude la creazione della Promessa, che può quindi essere passata come un valore. Quando tale valore viene passato in giro, il consumatore di tale valore non ha bisogno di resolve e reject e quindi tali funzioni non dovrebbero far parte dell'API pubblica.

(e tutta quella roba sulla gestione delle eccezioni e concatenamento)