2015-05-03 8 views
6

Ho il seguente programma: io uso genny.js per gestire il controllo di flusso asincrono - ho provato lo stesso con suspend.js - errore simile.Perché il mio iteratore è di nuovo avanzato?

Sto usando l'API nodejs di stripe.

La mia funzione di iteratore sembra essere chiamata due volte - che causa un errore - e non capisco perché viene chiamata due volte. Deve essere un semplice trucco mentale che non vedo.

var genny = require('genny') 
genny.longStackSupport = true 

var stripe = require("stripe")("sk_live_....") 

fetchCharges = genny.fn(function* (d) { 
    console.log("Before fetchCharges") 
    var charges = yield fetchList(d()) 
    console.log("After fetchCharges - found ", charges.length) 
    return true 
}) 

fetchList = genny.fn(function* (done) { 
    console.log("before fetchList") 
    var results = yield stripe.charges.list({}, done()) 
    console.log("after fetchList") 
    return results.data 
}) 

genny.run(function* (resume) { 
    console.log('before run') 
    yield fetchCharges(resume()) 
    console.log('after run') 
}) 

L'uscita della console è:

> node --harmony genny.js 

before run 
Before fetchCharges 
before fetchList 
after fetchList 
After fetchCharges - found 10 
after run 
/Volumes/dev/ingest/node_modules/genny/index.js:50 
     else throw e; 
       ^
Error: callback already called 
    at resume (/Volumes/dev/ingest/node_modules/genny/index.js:154:39) 
    at throwAt (/Volumes/dev/ingest/node_modules/genny/index.js:49:30) 
    at resume (/Volumes/dev/ingest/node_modules/genny/index.js:153:28) 
    at tryProcessPending (/Volumes/dev/ingest/node_modules/genny/index.js:41:28) 
    at resume (/Volumes/dev/ingest/node_modules/genny/index.js:164:17) 
    at null._onTimeout (/Volumes/dev/ingest/node_modules/stripe/lib/StripeResource.js:87:34) 
    at Timer.listOnTimeout (timers.js:110:15) 
From generator: 
    at /Volumes/dev/ingest/genny.js:22:26 

Ora, se sostituisco fetchList con la seguente funzione funziona benissimo:

fetchList = genny.fn(function* (done) { 
    console.log('before doTimeout') 
    console.log('1sec break ...') 
    yield setTimeout(done(), 1000); 
    console.log('after doTimeout') 
    return [] 
}) 

L'uscita della console è:

> node --harmony genny.js 

before run 
Before fetchCharges 
before doTimeout 
1sec break ... 
after doTimeout 
After fetchCharges - found 0 
after run 

Per illustrare ulteriormente il fatto che il metodo next() di itertor viene chiamato due volte - ho un'altra versione (non funzionante) del programma.

var genny = require('genny') 
genny.longStackSupport = true 

var stripe = require("stripe")("sk_live_...") 

fetchCharges = genny.fn(function* (d) { 
    console.log("Before fetchCharges") 
    var charges = yield fetchList(function(err, cb) { 
    console.log("callback") 
    }) 
    console.log("After fetchCharges - found ", charges.length) 
    return true 
}) 

fetchList = genny.fn(function* (done) { 
    console.log("before fetchList") 
    var results = yield stripe.charges.list({}, done()) 
    console.log("after fetchList") 
    return results.data 
}) 

genny.run(function* (resume) { 
    console.log('before run') 
    yield fetchCharges(resume()) 
    console.log('after run') 
}) 

Ed è uscita della console è qui:

> node --harmony genny.js 

before run 
Before fetchCharges 
before fetchList 
after fetchList 
callback 
callback 

E 'strano - e io non capisco. Può qualcuno più intelligente di me, per favore, spiegami.

UPDATE

ho cambiato il codice per chiamare i metodi della banda senza un callback o la funzione iteratore ripresa. E ora funziona. MA - curiosamente - guarda la console ai "risultati". Non capisco perché. Quindi ora non chiama la funzione next() di nexter dell'erogatore di fetch() "ma non vedo dove viene chiamata anche una volta !?

var results = yield stripe.charges.list() 

Ecco il programma completo aggiornato.

var genny = require('genny') 
genny.longStackSupport = true 

var stripe = require("stripe")("sk_live_i6TrEk5lSRM1CmbSZZPsQzKc") 

fetchCharges = genny.fn(function* (d) { 
    console.log(" fetchCharges {") 
    var charges = yield fetchList(d()) 
    console.log(" } fetchCharges - found ", charges.length) 
    return true 
}) 

fetchList = genny.fn(function* (done) { 
    console.log(" fetchList {") 
    var results = yield stripe.charges.list({}, function(err, results) { 
    console.log("results ") 
    }) 
    console.log(" } fetchList") 
    return results.data 
}) 

genny.run(function* (resume) { 
    console.log('Before run {') 
    yield fetchCharges(resume()) 
    console.log('} after run') 
}) 

Ciò restituisce

> node --harmony genny.js 

Before run { 
    fetchCharges { 
    fetchList { 
    } fetchList 
    } fetchCharges - found 10 
} after run 
results 
+0

Sei sicuro che 'stripe.charges.list' non chiama più la sua richiamata? – Bergi

+0

Controlla se i risultati di 'yield stripe.charges.list (...)' hanno effettivamente senso (cioè se hanno effettivamente una matrice 'data'). – robertklep

+0

Ho aggiornato la domanda con il codice dove non ho nemmeno fornito una funzione callback o resume() alla chiamata stripe - e ora funziona. Non riesco a dare un senso a questo però. Ho sostituito brevemente la funzione resume() che ho passato come richiamata alla chiamata stripe con una funzione di callback ed è stata chiamata solo una volta (forse perché non ho fatto avanzare l'iteratore dopo questo?) – Joerg

risposta

2

Il problema si verifica deriva da un mix di due approcci per asincronia.

Il stripe API docs menziona

Ogni metodo risorsa accetta un callback facoltativa come ultimo argomento.
Inoltre, ogni metodo di risorsa restituisce una promessa.

Tuttavia, sia genny e suspend fare

lavoro senza soluzione di continuità con il nodo convenzioni callback e promesse

E qui sta l'errore: nella linea

var results = yield stripe.charges.list({}, done()) 

y o0 implicitamente utilizzare entrambi allo stesso tempo. Lo done() crea un callback che viene passato a stripe, ma quella chiamata produce anche una promessa che viene restituita, e genny/suspend registra un'altra callback su di esso. Questo porta al Error: callback already called che stai osservando.

Si può scegliere come si vuole risolvere il problema:

  • non danno la promessa

    var results = yield void stripe.charges.list({}, done()) 
    //     ^^^^ 
    
  • non passa una richiamata

    var results = yield stripe.charges.list({}) 
    

(io consiglierei quest'ultimo)

+0

È esattamente così! Grazie mille per la tua risposta dettagliata. Posso continuare con la mia vita ora. – Joerg