2015-05-03 4 views
6

Ich habe das folgende Programm - Ich benutze genny.js, um die asynchrone Ablaufsteuerung zu behandeln - Ich habe das gleiche mit suspend.js - ähnlicher Fehler versucht.Warum wird mein Iterator wieder vorgerückt?

Ich verwende die Stripe nodejs API.

Meine Iterator-Funktion scheint zweimal aufgerufen zu werden - was einen Fehler verursacht - und ich verstehe nicht, warum sie zweimal aufgerufen wird. Es muss ein einfacher Geistestrick sein, den ich nicht sehe.

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') 
}) 

Die Konsolenausgabe ist:

> 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 

Nun, wenn ich fetchList mit der folgenden Funktion ersetzen, es funktioniert:

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

Die Konsolenausgabe ist:

> node --harmony genny.js 

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

Um die Tatsache weiter zu veranschaulichen dass die next() - Methode des itertors zweimal aufgerufen wird - ich habe eine andere (nicht funktionierende) Version des Programms.

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') 
}) 

Und es ist die Konsolenausgabe ist hier:

> node --harmony genny.js 

before run 
Before fetchCharges 
before fetchList 
after fetchList 
callback 
callback 

Es ist komisch - und ich verstehe es nicht. Kann jemand, der klüger ist als ich, bitte erklären.

UPDATE

Ich habe den Code geändert, um die Streifen Methoden ohne einen Rückruf oder Iterator Resume-Funktion aufzurufen. Und jetzt funktioniert es. ABER - neugierig - schauen Sie sich die Konsole bei den "Ergebnissen" an. Ich verstehe nicht warum. Jetzt ruft es die next() - Funktion des fetchList-Iterators nicht "ein zweites Mal" auf - aber ich sehe nicht, wo es einmal aufgerufen wird !?

var results = yield stripe.charges.list() 

Hier ist das aktualisierte vollständige Programm.

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') 
}) 

Das gibt

> node --harmony genny.js 

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

Sind Sie sicher, dass 'stripe.charges.list' seinen Rückruf nicht mehrmals aufruft? – Bergi

+0

Überprüfen Sie, ob die Ergebnisse von 'yield stripe.charges.list (...)' tatsächlich sinnvoll sind (dh wenn sie tatsächlich ein 'data' Array haben). – robertklep

+0

Ich habe die Frage mit Code aktualisiert, wo ich nicht einmal eine Callback- oder resume() - Funktion für den Stripe-Aufruf anbiete - und jetzt funktioniert es. Ich kann es aber nicht verstehen. Ich habe kurz die resume() -Funktion, die ich als Callback an den Stripe-Aufruf mit einer Callback-Funktion übergebe und die nur einmal aufgerufen wurde (vielleicht weil ich den Iterator danach nicht weiterentwickelt habe), ersetzen. – Joerg

Antwort

2

Das Problem, das Sie erleben aus einer Mischung von zwei Ansätzen für Asynchronität Stielen.

Die stripe API docs erwähnt

Jede Ressource Verfahren einen optionalen Rückruf als letztes Argument annimmt.
Darüber hinaus gibt jede Ressourcenmethode ein Versprechen zurück.

jedoch beide genny und suspendund Versprechen

Und hier liegt Ihre Fehler nahtlos mit Knoten Callback-Konventionen

Arbeit tun: in der Linie

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

y Verwenden Sie implizit gleichzeitig. Die done() erstellt einen Rückruf, der an stripe übergeben wird, aber dieser Aufruf erzeugt auch eine Zusage, die zurückgegeben wird, und genny/suspend registriert einen weiteren Rückruf darauf. Dies führt zu der Error: callback already called, die Sie beobachten.

Sie können wählen, wie Sie das Problem beheben möchten:

  • ergeben keine Versprechen

    var results = yield void stripe.charges.list({}, done()) 
    //     ^^^^ 
    
  • nicht passieren einen Rückruf

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

(Ich würde letzteres empfehlen)

+0

Das ist genau das! Vielen Dank für Ihre ausführliche Antwort. Ich kann jetzt mit meinem Leben weitermachen. – Joerg

Verwandte Themen