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