Lingua compilata per JS - il modo più elegante per fare attese in stile sincrono

8

Sto cercando di creare (ancora un altro) linguaggio che compaia in JavaScript. Una delle funzionalità che mi piacerebbe avere è la possibilità di eseguire le operazioni asincrone di JavaScript in modo sincrono (non esattamente in modo sincrono - senza bloccare il thread principale, ovviamente). Meno chiacchiere, altri esempi:

/* These two snippets should do exactly the same thing */

// the js way
var url = 'file.txt';
fetch(url)
    .then(function (data) {
        data = "(" + data + ")";
        return sendToServer(data);
    }).then(function (response) {
        console.log(response);
    });

// synchronous-like way
var url = 'file.txt';
var response = sendToServer("(" + fetch(url) + ")");
console.log(response);

Qual è il modo più elegante per ricompilarlo in JS?

I criteri, ordinati per importanza:

  1. Prestazioni
  2. Compatibilità con le versioni precedenti
  3. Leggibilità del codice compilato (non molto importante)

Ci sono diversi modi possibili per implementarlo, quei tre mi sono venuti in mente:

  1. Uso delle promesse:

    • f(); a( b() ); si trasformerebbe in
    • f().then(function(){ return b() }).then(function(x){ a(x) });
    • pros: relativamente semplice da implementare, polyfillable torna a ES3
    • contro: creazione di una nuova funzione e un'istanza Proxy per ogni chiamata di funzione
  2. Uso dei generatori :

    • f(); a( b() ); si trasformerebbe in
    • yield f(); tmp = yield b(); yield a( tmp );
    • pros: javascript più bello
    • contro: creazione di proxy, generatori e iteratori su ogni passaggio, anche non polifunzionabile
    • EDIT: infatti sono polifunibili utilizzando un altro ricompilatore (ad esempio Regenerator )
  3. Uso di XMLHttpRequest sincrono:

    • richiede uno script server che attende fino a un'altra chiamata da qualche altra parte nel codice
    • pro: de facto nessun cambiamento nel codice, super facile da implementare
    • contro: richiede lo script del server (= > nessuna portabilità, solo browser); inoltre, synchronous XMLHttpRequest blocca il thread in modo che non funzioni affatto
    • EDIT: in realtà funzionerebbe all'interno di un lavoratore

Il problema principale è che non è possibile stabilire se una funzione è asincrona fino al runtime. Ciò si traduce in due soluzioni che almeno funzionano sono molto più lente del puro JS. Quindi chiedo:

Esistono modi migliori per implementare?

Dalle risposte:

Attendi la parola chiave (@ MI3Guy)

  • C # way (che è buono !)
  • introduce una nuova parola chiave (che può essere mascherata come una funzione (cittadino di prima classe) che ad esempio lancia se il programmatore prova a passarlo come argomento)
  • Come sapere che la funzione è asincrona? Nessun'altra parola chiave!
    • Ogni funzione che contiene la parola chiave await diventa asincrona?
    • Quando il codice raggiunge await , restituisce una promessa? Altro tratta come una funzione ordinaria?
posta m93a 08.04.2015 - 18:51
fonte

1 risposta

3

Supponendo che la lingua sia tipizzata dinamicamente, posso vedere due modi diversi in cui questo potrebbe essere gestito senza sapere in fase di compilazione se una chiamata è asincrona.

Un approccio sarebbe assumere che ogni chiamata potrebbe essere asincrona. Tuttavia, questo sarebbe incredibilmente inefficiente e produrrà un sacco di codice extra. Questo è essenzialmente ciò che stai facendo nell'opzione 2.

Un'altra opzione è usare le fibre. Le fibre sono come fili leggeri programmati dal programma. La fibra stessa decide quando rinunciare al controllo. Tuttavia, questi richiedono il supporto del sistema operativo. Per quanto ne so, l'unico modo per usare le fibre in JavaScript è all'interno di node.js. La mia ipotesi è che se si sta compilando a JS che l'obiettivo (o almeno un obiettivo) è quello di eseguire sul browser che esclude questa opzione. Questa sarebbe una versione più leggera dell'opzione 3.

Onestamente, prenderei in considerazione l'aggiunta di una parola chiave come await di C #. Questo ha una serie di vantaggi oltre a specificare che una funzione è asincrona. Le funzioni possono essere chiamate per produrre futuri senza attendere immediatamente. Ciò consente di eseguire più operazioni asincrone contemporaneamente anziché in sequenza. Inoltre, è meglio essere espliciti su quali operazioni sono asincrone perché un altro codice può essere eseguito mentre l'operazione è in corso. Se async è automatico, potrebbe essere sorprendente quando alcuni dati che stai utilizzando sono modificati da un pezzo di codice esterno.

In C # all'interno di un metodo contrassegnato come asincrono, l'istruzione return viene utilizzata per restituire il valore che sarà all'interno del futuro, non il futuro stesso. Si noti, tuttavia, che il modificatore asincrono non fa realmente parte della firma del metodo. await può essere utilizzato con qualsiasi metodo che restituisce un futuro.

Se non desideri aggiungere un modificatore asincrono per le funzioni, puoi decidere (come hai affermato) che qualsiasi funzione con un'attesa viene trattata come se avesse un modificatore asincrono implicito. Quindi, anche se una funzione ha una await , ma ritorna prima di raggiungerla, la funzione dovrebbe comunque restituire un futuro. Vorrei anche esporre un modo per convertire un valore in un futuro completo che contenga il valore, specialmente se non c'è modo di farlo implicitamente aggiungendo il modificatore asincrono.

    
risposta data 09.04.2015 - 01:20
fonte