Programación en JavaScript/Generadores

Los generadores son parte de las especificaciones del estandar ECMAScript 6. Firefox soporta la sintaxis de los generadores de ECMAScript 6 desde el 10 de Octubre de 2013.[1] Node.js (Javascript server-side) también soporta la sintanxis para generadores.[2] desde el 15 de Mayo de 2013.

Los generadores pueden retornar uno o varios valores pausando su rutina sin terminarla. Además se les pueden pasar parámetros cada vez que se resume su rutina.[3]

La palabra clave yield se podría interpretar como un return que retorna un valor un estado cuando se llama al metodo next() del generador. Para retornar un valor cuando se llama a next(), en el código del generador se le debe colocar la expresión de retorno a la derecha de la palabra yield. Por ejemplo: yield b; primero retorna la variable b y segundo pausa el flujo de ejecucion de la funcion a la espera de la proxima llamada a next().

La palabra clave yield tambien se puede usar para recibir parámetros cada vez que se resume el flujo de ejecución de la función. Sólo se le debe pasar un parámetro a next(parametro), y en el código asignarle yield a alguna variable o usar dicha palabra clave como parámetro de alguna función. Por ejemplo: var a = yield b; primero retorna la variable b, segundo pausa el flujo de ejecución de la función a la espera de la próxima llamada a next(), y tercero (en la siguiente llamada a next()) asigna el parámetro a la variable a.

La palabra clave yield siempre pausa el flujo de ejecución y retorna un valor, aunque se use para recibir un parametro. Cuando no se pasa un parametro, yield asigna un undefined. Cuando no se especifica un valor de retorno, yield retorna un undefined.

El metodo next() siempre retorna un objeto con los atributos value que es el valor retornado por yield o por return en un generador, y el atributo done que con false indica que el value proviene de un yield mientras que true indica que proviene del return. Una vez que se llega al return, todas las demas llamadas a next() devolveran un undefined.[4][5]

Ejemplos

editar

Un ejemplo simple con retorno con yield y recepción de parámetros:

function* gen() 
{ 
	var a=0,b=undefined,c=0; 
	
	while(a<20)
	{ 
		if(b!=undefined) 
		{ 
			a=b; // resetea la variuable "a" para que empiece desde el valor indicado como parametro en yield
			c++; // suma un reset mas a la cantidad total hasta ahora
		} 
		b=yield a+','+c; // retorna el valor de "a" concatenado con "c" y en el siguiente next()recibe un valor para "b"
		a++; 
	} 
	return 'yield terminados. Esto es el return.';
} 

var secuencia = gen();

secuencia.next(); // Object { value="0,0",  done=false}
secuencia.next(); // Object { value="1,0",  done=false}
secuencia.next(); // Object { value="2,0",  done=false}
secuencia.next(10); // Object { value="10,1",  done=false} 
secuencia.next(); // Object { value="11,1",  done=false}
secuencia.next(); // Object { value="12,1",  done=false}
secuencia.next(5); // Object { value="5,2",  done=false} 
secuencia.next(); // Object { value="6,2",  done=false}
secuencia.next(19); // Object { value="19,3",  done=false} 
secuencia.next(); // Object { value="20,3",  done=false}
secuencia.next(); // Object { value="yield terminados. Esto es el return.",  done=true}

La palabra clave function* crea una función de tipo generador.

Cuando la palabra clave yield se usa para retornar un valor y para asignar un valor, todo en la misma expresion, es importante tener en cuenta el orden en que se ejecutan ambas operaciones. Hay que tener en cuenta que cuando ambas operaciones estan en una misma expresion con yield, siempre el retorno se ejecuta primero ante una llamada del metodo next() y la asignacion despues en la siguiente llamada del metodo next(). Esto es lógico, ya que que el flujo de ejecucion se pausa despues que yield retorna un valor sin llegar a la asignacion. En la próxima llamada al metodo next() se resume el flujo de ejecucion desde este punto, continuando con la asignacion y el resto de las instrucciones hasta el siguiente yield o return del generador.

Ejemplo dodne se puede ver la importancia del orden de ejecucición en una expresión con yield:

function *crearGenerador() {
    let primero = yield 1;
    let segundo = yield primero + 2;       // 4 + 2
    yield segundo + 3;                   // 5 + 3
}

let generador = crearGenerador();

console.log(generador.next());           // "{ value: 1, done: false }"
console.log(generador.next(4));          // "{ value: 6, done: false }"
console.log(generador.next(5));          // "{ value: 8, done: false }"
console.log(generador.next());           // "{ value: undefined, done: true }"+

References

editar