Opóźnianie powrotu funkcji asynchronicznej w REPL

Jeśli podczas korzystania z REPL ( http://nodejs.org/api/repl.html ) używamy funkcji asynchronicznej możemy trafić na mały problem.
Funkcja wywoływana z REPL po zakończeniu swojego działania i zwróceniu wartości jest uznawana za całkowicie wykonaną i zostaje odblokowana możliwość wprowadzania nowych poleceń. W przypadku funkcji asynchronicznej może dochodzić do dziwnych sytuacji kiedy
to funkcja zwróci swój wynik użytkownik zdążył już coś wprowadzić i wszystko się rozjeżdża.

Co jeśli chcemy aby REPL poczekał do zakończenia działania funkcji asynchronicznej i zwrócenia wartości ?
Nie jest to takie łatwe standardowo nie dostajemy żadnej możliwości kontrolowania co się dzieje ( callbacka etc. )
jednak przychodzi nam z pomocą możliwość definiowania własnej funkcji wykonywanej przy każdym wykonaniu polecenia.

repl.start(options)

jedna z opcji to

eval - function that will be used to eval each given line. Defaults to an async wrapper for eval(). See below for an example of a custom eval.

Przykład

function eval(cmd, context, filename, callback) {
callback(null, result);
}

Ostatni argument to callback który powinnien być wykonany kiedy chcemy aby REPL przywrócił możliwość wprowadzania poleceń.

Napisałem małą klasę która pozwoli w łatwy sposób to wszystko kontrolować

var AsynchronousHandle = new function (){
this._callback = null;
this._mutexBlock = false;

this.setCallback = function( callback ){
this._callback = callback;
}

this.runCallback = function( result ){
this._callback( null , result == undefined ? null : result );
}

this.handleEval = function( cmd, context, filename, callback ){
this.unblockMutex();
this.setCallback( callback );

eval( cmd );

if( !this.mutexIsBlocked() ){
this.runCallback( null );
}
}

this.blockMutex = function(){
this._mutexBlock = true;
}

this.unblockMutex = function(){
this._mutexBlock = false;
}

this.mutexIsBlocked = function(){
return this._mutexBlock;
}
}


Przykład użycia

require( 'repl' ).start( {
useColors: true,
input: process.stdin,
output: process.stdout,
eval: AsynchronousHandle.handleEval.bind( AsynchronousHandle )
});

function testFunction(){
AsynchronousHandle.blockMutex();

console.log( 'testFunction' );

setTimeout( AsynchronousHandle.runCallback.bind( AsynchronousHandle ) , 1000 );
}

Metody klasy

  • setCallback – ustawia callback który zostanie wywołany
  • runCallback – uruchamia callback
  • handleEval – metoda która obsługuje nowy eval podawany opcjach startowania REPL
  • blockMutex – blokuje mutex
  • unblockMutex – odblokowuje mutext

Sposób użycia
Jest to w sumie bardzo proste
podczas startowania REPL jako eval podajemy metode handleEval z klasy AsynchronousHandle
np.

require( 'repl' ).start( {
useColors: true,
input: process.stdin,
output: process.stdout,
eval: AsynchronousHandle.handleEval.bind( AsynchronousHandle )
});

Potem kiedy funkcja asynchroniczna zaczyna być wywoływana blokujemy mutext

AsynchronousHandle.blockMutex()

Kiedy wszystko zostanie już wykonane i zwrócone uruchamiamy runCallback z AsynchronousHandle
np.

AsynchronousHandle.runCallback.bind( AsynchronousHandle )

podczas wykonywania runCallback możemy podać w 1 parametrze co funkcja zwróci ale nie musimy tego robić w tym przypadku zwróci null

Dodaj komentarz

Witryna wykorzystuje Akismet, aby ograniczyć spam. Dowiedz się więcej jak przetwarzane są dane komentarzy.