Generadores ECMA6: promesa de rendimiento

Según tengo entendido, se supone que los generadores de ECMA6 pueden rendirse a una función que devuelve una promesa, y finalmente devuelve el resuelto / rechazado. Hacer que el código se lea más como código síncrono y evitar el infierno de callback.

Estoy usando node.js v0.12.2 con –harmony y el siguiente código.

var someAsyncThing = function() { return new Promise(function(resolve, reject) { resolve("I'm Resolved!"); }); }; someAsyncThing().then(function(res) {console.log(res);}); // Works as expected: logs I'm Resolved! function* getPromise() { var x = yield someAsyncThing(); console.log("x: " + x); // Fails x undefined } var y = getPromise(); console.log(y); // returns {} console.log(y.next()); // Fails: logs { value: {}, done: false } 

He basado el código en los pocos ejemplos que he podido encontrar en línea. ¿Qué estoy haciendo mal?

Según tengo entendido, se supone que los generadores de ECMA6 pueden rendirse a una función que devuelve una promesa, y finalmente devuelve el resuelto / rechazado.

No, ese no es su propósito. Se supone que los generadores de ES6 proporcionan una forma sencilla de escribir iteradores: cada llamada a una función de generador produce un iterador. Un iterador es solo una secuencia de valores, como una matriz, pero consumida dinámicamente y producida perezosamente.

Ahora, se puede abusar de los generadores para el flujo de control asíncrono, produciendo una secuencia de promesas que se consumen de forma asíncrona y avanzando el iterador con los resultados de cada promesa esperada. Vea aquí una explicación sin promesas.

Entonces, lo que falta en su código es el consumidor que realmente espera las promesas y adelanta su generador. Por lo general, usaría una biblioteca dedicada (como co o task.js ), o una de las funciones de ayuda que muchas bibliotecas de promesa proporcionan ( Q , Bluebird , cuando , …), pero para los propósitos de esta respuesta, le mostraré una uno simplificado:

 function run(gf) { let g = gf(); return Promise.resolve(function step(v) { var res = g.next(v); if (res.done) return res.value; return res.value.then(step); }()); } 

Ahora, con esta función, realmente puede “ejecutar” su generador getPromise :

 run(getPromise).then(console.log, console.error); 

tl; dr: La promesa producida por el generador tiene que mover el generador hacia adelante.


Si observa los primeros ejemplos en http://davidwalsh.name/async-generators , notará que la función asíncrona realmente mueve el iterador hacia adelante:

 function request(url) { // this is where we're hiding the asynchronicity, // away from the main code of our generator // `it.next(..)` is the generator's iterator-resume // call makeAjaxCall( url, function(response){ it.next( response ); // <--- this is where the magic happens } ); // Note: nothing returned here! } 

Pero como está trabajando con promesas, podemos mejorar eso un poco. y.next().value devuelve una promesa, y tendrías que escucharla. Entonces, en lugar de escribir console.log(y.next()) , escribirías:

 var promise = y.next().value; promise.then(y.next.bind(y)); // or promise.then(function(v) { y.next(v); }); 

Demo de babel

Por supuesto, esto no es muy práctico, porque ahora no puede acceder al siguiente valor de rendimiento y no sabe cuándo se realizará el generador. Sin embargo, podrías escribir una función recursiva que se encargue de eso. De eso se runGenerator introducido más adelante en este artículo.