Why is my iterator moving forward again?

I have the following program - I use genny.js to handle asynchronous flow control - I tried the same with suspend.js - a similar error.

I am using Stripe nodejs API.

My iterator function seems to be called twice - this causes an error - and I don't understand why it is called twice. It should be a simple trick that I do not see.

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

Console output:

 > 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 

Now, if I replaced fetchList with the following function, it works fine:

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

Console output:

 > node --harmony genny.js before run Before fetchCharges before doTimeout 1sec break ... after doTimeout After fetchCharges - found 0 after run 

To show once again that the itertor next () method is called twice - I have another (non-working) version of the program.

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

And he displays the console here:

 > node --harmony genny.js before run Before fetchCharges before fetchList after fetchList callback callback 

This is strange - and I do not understand. Can someone smarter than me, please explain.

UPDATE

I changed the code to call stripe methods without a callback or the iterator resume function. And now it works. BUT - curious - look at the console for "results." I do not understand why. So now it doesnโ€™t call the fetchList iterator next () function โ€œsecond timeโ€, but I donโ€™t see where it is even called once !?

 var results = yield stripe.charges.list() 

Here's the updated full program.

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

It returns

 > node --harmony genny.js Before run { fetchCharges { fetchList { } fetchList } fetchCharges - found 10 } after run results 
+6
source share
1 answer

The problem you are facing is related to a combination of two approaches for asynchrony.

stripe API docs mentions

Each resource method accepts an optional callback as the last argument.
In addition, each resource method returns a promise.

However genny and suspend do

works seamlessly with Node callbacks and promises

And here is your mistake: in line

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

you are implicitly using both at the same time. done() creates a callback that is passed to the strip, but this call also gives a promise that gives way, and genny / suspend registers another callback on it. This results in the Error: callback already called that you are observing.

You can choose how you want to fix the problem:

  • don't make promises

     var results = yield void stripe.charges.list({}, done()) // ^^^^ 
  • do not pass callback

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

(I would recommend the latter)

+2
source

Source: https://habr.com/ru/post/986439/


All Articles