2010-10-27 8 views
13

Uno de los nuevos mecanismos disponibles en JavaScript 1.7 es yield, útil para generadores e iteradores.¿Cómo simular el rendimiento de JavaScript?

Actualmente solo es compatible con los navegadores de Mozilla (que yo conozco). ¿Cuáles son algunas de las formas de simular este comportamiento en navegadores donde no está disponible?

+1

Aunque se responde a esta pregunta, me sorprende que nadie haya mencionado [regenerator] (https://facebook.github.io/regenerator/) aún. –

Respuesta

18

Bueno, siempre se puede escribir una función externa que inicializa las variables en un cierre y luego devuelve un objeto que hace el trabajo que desee.

function fakeGenerator(x) { 
    var i = 0; 
    return { 
    next: function() { 
     return i < x ? (i += 1) : x; 
    } 
    }; 
} 

Ahora se puede escribir:

var gen = fakeGenerator(10); 

y luego llamar a gen.next() una y otra vez. Sería complicado simular el comportamiento "final" del método "close()" en los generadores reales, pero es posible que puedas acercarte a él.

2

sin algún tipo de compilador o preprocesador ... No.

Lo más cerca que se puede llegar es algo como esto:

function doStuff() { 
    var result = { }; 
    function firstStuf() { ...; result.next = secondStuff; return 42; }; 
    function secondStuf() { ...; result.next = thirdStuff; return 16; }; 
    function thirdStuf() { ...; result.next = null; return 7; }; 
    result.next = firstStuff; 
    return result; 
} 

Pero, bueno ... Eso es bastante malo, y realmente no es mucho de un sustituto.

7

Similar a la respuesta de puntiagudo, pero con un método hasNext:

MyList.prototype.iterator = function() { //MyList is the class you want to add an iterator to 

    var index=0; 
    var thisRef = this; 

    return { 
     hasNext: function() { 
      return index < thisRef._internalList.length; 
     }, 

     next: function() { 
      return thisRef._internalList[index++]; 
     } 
    }; 
}; 

de la let hasNext método que bucle como:

var iter = myList.iterator() //myList is a populated instance of MyList 
while (iter.hasNext()) 
{ 
    var current = iter.next(); 
    //do something with current 
} 
+1

Gracias Robert, +1 por la adición de hasNext. – sworoc

+0

Estoy en lo correcto a utilizar? Función Mi lista (arr) { this.arr = arr } MyList.prototype.iterator = function() {// Mi lista es la clase que desea añadir un iterador a depurador índice var = 0; var thisRef = this.arr; return { hasNext: function() { índice de retorno zloctb

+0

@zloctb, Algo difícil de leer en un comentario, y hace tres años que no estoy en una posición de JS, pero parece correcto. ¿Funciona? –

2

He comenzado un pequeño proyecto que trata de hacer esto con un poco de truco de devolución de llamada. Puesto que es imposible crear corrutinas reales en "estándar" JavaScript, esto no está exento de algunas advertencias, por ejemplo:

  • que es imposible hacer esto sigue el protocolo de iteración (cosas como .next() etc.),
  • es imposible iterar a través de varios generadores a la vez,
  • usted tiene que estar atento a no dejar que los objetos equivocados salen del alcance del generador (por ejemplo, llamando yield en un tiempo de espera – ya que esto es "normal" JavaScript, no hay restricción de sintaxis que le impide hacer esto),
  • exce Las funciones en el generador son un poco complicadas,
  • y, por último, es muy experimental (esto comenzó hace unos días).

Del lado positivo, tiene yield! :)

El ejemplo de Fibonacci de la MDC page se vería así:

var fibonacci = Generator(function() { 
    var fn1 = 1; 
    var fn2 = 1; 
    while (1){ 
    var current = fn2; 
    fn2 = fn1; 
    fn1 = fn1 + current; 
    this.yield(current); 
    } 
}); 

console.log(fibonacci.take(10).toArray()); 

Salida:

[1, 1, 2, 3, 5, 8, 13, 21, 34, 55] 

El proyecto está en BitBucket en https://bitbucket.org/balpha/lyfe.

4

Para una función de generador no trivial, querrá utilizar algún tipo de herramienta para traducir su código a un equivalente de ES3 para que pueda ejecutarse en cualquier navegador moderno. Recomiendo probar Traceur, que puede describirse aproximadamente como un traductor fuente de ES6 a ES3. Debido a que los generadores son una función de lenguaje destinada a ES6, Traceur podrá traducirlos por usted.

Traceur proporciona un demo page donde puede escribir el código ES6 y ver el ES3 generado sobre la marcha. Si introduce algo tan simple como:

// Note that this declaration includes an asterisk, as specified by current ES6 
// proposals. As of version 16, Firefox's built-in support for generator 
// functions does not allow the asterisk. 
function* foo() { 
    var n = 0; 
    if (n < 10) { 
    n++; 
    yield n; 
    } 
} 

for (var n of foo()) { 
    console.log(n); 
} 

verá que el código ES3 equivalente no es trivial y requiere traceur.runtime ser incluido para que el código se ejecuta correctamente en un navegador. El tiempo de ejecución se define en http://traceur-compiler.googlecode.com/git/src/runtime/runtime.js, que actualmente es 14K (no modificado). Esta es una cantidad de código no trivial, aunque es probable que gran parte de ella pueda optimizarse utilizando el compilador de cierre.

Tenga en cuenta que también se ha archivado un error para proporcionar una opción para alinear las funciones requeridas del espacio de nombre traceur.runtime, lo que eliminaría la necesidad de incluir runtime.js en total: https://code.google.com/p/traceur-compiler/issues/detail?id=119.

Cuestiones relacionadas