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