È possibile sincronizzare a metà strada le funzioni javascript che includono le chiamate asincrone?

4

Mi chiedo se esista un modo per sincronizzare a metà le funzioni di javascript, dove i dati sono richiesti al volo. Non voglio renderlo puramente bloccante (cosa che immagino sia impossibile), voglio solo un bel modo per scrivere l'intero algoritmo in un unico pezzo.

Facciamo il seguente esempio:

function(){
    command1;
    var x1 = something;
    async_function(x1, callback1);
}
function callback1(data){
    var x2 = command2(data);
    async_function2(x2, callback2);
}
function callback2(data1){
    command3(data1);
}

Vorrei scrivere qualcosa come:

function(){
    command1;
    var x1 = something;
    call(async_function, [x1], callback1);
    var x2 = command2(data);
    call(async_function2, [x2], callback2);
    command3(data1);
}
  • Ho provato a giocare con la proprietà caller delle funzioni, ma non ho familiarità con l'ambiente di esecuzione.
  • Ho anche provato a scrivere una funzione "call" come sopra, usando la funzione apply per passare una chiamata personalizzata che restituisce il risultato della funzione asincrona al suo chiamante.

Ciò che mi infastidisce è che durante la programmazione di \ debugging, devo seguire il codice da una funzione a un'altra in un'altra (proprio come l'inizio del film). Voglio un posto in cui scrivere l'algoritmo di alto livello senza decomporlo in molte funzioni.
Ad esempio, se voglio scrivere un algoritmo A *, ma la funzione "getNeighbours" è asincrona.

    
posta Ramzi Kahil 26.02.2013 - 14:19
fonte

6 risposte

5

Dato che quello di cui stai parlando qui è un problema stilistico / di leggibilità (almeno, è così che lo leggo), potresti essere un po 'sfortunato, perché tutti i cambiamenti che posso pensare non sono grande.

In realtà hai due opzioni, scrivi più funzioni distinte come hai fatto sopra, o usi chiusure per portare tutto "in linea" (per così dire), dove ti imbatterai rapidamente in problemi con il mantenimento degli spazi bianchi orizzontali.

Detto questo, se davvero vuoi mantenere tutto in un unico blocco che sembra una procedura, probabilmente andrei con l'opzione di chiusura:

function(){
    command1;
    var x1 = something;
    async_function(x1, function (data){
        var x2 = command2(data);
        async_function2(x2, function (data1){
            command3(data1);
        });
    });
}

Che ovviamente potrebbe essere distanziato in questo modo, abbastanza vicino al tuo esempio desiderato:

function(){
    command1;
    var x1 = something;
    async_function(x1, function (data){
    var x2 = command2(data);
    async_function2(x2, function (data1){
    command3(data1);
    });});
}

... ma personalmente probabilmente proverei a seguire la prima versione.

In generale, anche se non lo farei. Lo terrei suddiviso in routine piccole e ovviamente nominate, trovo che rende a lungo andare codice molto più leggibile e qualsiasi IDE sano ti permetterà di passare dalla chiamata di funzione alla definizione della funzione e viceversa, quindi la navigazione non dovrebbe essere troppo di un problema.

    
risposta data 26.02.2013 - 14:47
fonte
4

Non posso garantire che ciò risolva i problemi che stai incontrando, ma Accodamento viene spesso utilizzato per concatenare una serie di chiamate asincrone in modo che si verifichino una dopo l'altra.

Ho scritto uno script Queue qualche tempo fa che userò come esempio:

(function () {
    var q = new Queue();
    q.queue(function (next) {
        //this gets run first
        //next is used to dequeue the next item in the queue
        someAsyncCall(someData, /*callback*/next);
    }, function (next) {
        //this gets run second
        someOtherAsyncCall(moreData, next);
    }, function (next) {
        //this gets run third
        moarAsync(allthedata, next);
    });
}());

Le code consentono di scrivere il flusso del codice in modo lineare senza grandi quantità di nidificazione. Questa particolare implementazione non gestisce i dati che passano direttamente.

    
risposta data 26.02.2013 - 16:01
fonte
4

Dovresti esaminare Deferred .

Puoi implementare le tue funzioni ed eseguirle in sequenza, indipendentemente dal fatto che il codice all'interno della funzione sia asincrono. Una volta che il primo termina, il secondo verrà attivato e così via.

    
risposta data 27.02.2013 - 17:37
fonte
1

Se non ti dispiace aggiungere un passaggio di compilazione al tuo flusso di lavoro, puoi utilizzare uno dei compilatori sincrono-asincrono da questo elenco:

link

(vai alla sezione "Compilatori sincroni asincroni JavaScript")

L'uso di una soluzione basata su libreria è OK la maggior parte delle volte, ma l'utilizzo di un dialetto async-aware renderà il codice il più possibile "simile alla sincronizzazione". (questo è più evidente se si hanno molti loop o salti, come dichiarazioni di ritorno o di interruzione, in quei casi può essere noioso scrivere cose usando le continuazioni)

    
risposta data 27.02.2013 - 19:41
fonte
0

Se ti capisco bene, stai parlando di chiamate asincrone con piping.

Questo può essere fatto con i cosiddetti oggetti Deffered , come menzionato da @rusev. Ecco un esempio che combina insieme i blocchi e le cose asincrone:

var dfd = $.Deferred();

dfd.pipe(function (data) {
    // Normal blocking computations...
    var result = data.a + data.b;
    var output = $('<p>').text('Add: ' + result);
    $('body').append(output);

    return result;
}).pipe(function (data) {
    // Async AJAX call...
    return $.post('/echo/json/', { json: '{ "value": ' + data + ' }' });
}).pipe(function (data) {
    // Again some normal computations...
    var result = data.value * data.value;
    var output = $('<p>').text('Square: ' + result);
    $('body').append(output);

    return result;
}).pipe(function (data) {
    // AJAX...
    return $.post('/echo/json/', { json: '{ "value": ' + data + ' }' });
}).pipe(function (data) {
    // Normal...
    var result = data.value + 1;
    var output = $('<p>').text('Increment: ' + result);

    $('body').append(output);
});

dfd.resolve({a: 1, b: 2}); // Initial values

link

Come puoi vedere, i dati vengono passati sequenzialmente da un callback a un altro, ma non devi scriverlo in modo annidato.

    
risposta data 27.02.2013 - 21:13
fonte
0

Dai un'occhiata al link Promises / A + . La buona implementazione di Promises / A + invece del supporto nativo per ecma6 è link

    
risposta data 01.04.2015 - 16:10
fonte