Skip to content

Instantly share code, notes, and snippets.

@CreatCodeBuild
Created May 5, 2019 22:48
Show Gist options
  • Save CreatCodeBuild/3d46fc895ea5debbc7b3ddf92a52890d to your computer and use it in GitHub Desktop.
Save CreatCodeBuild/3d46fc895ea5debbc7b3ddf92a52890d to your computer and use it in GitHub Desktop.
Why "Generator is already running"? I need help!
const EventEmitter = require('events');
class MyEmitter extends EventEmitter { }
let e = new MyEmitter()
const DriveMainLoop = Symbol("DriveMainLoop");
// eventBus must be the kernel/task this sleeper belongs to
function Sleeper(eventBus, doneToken) {
return {
Sleep: function (time) {
setTimeout(function () {
eventBus.emit(doneToken)
}, time);
},
}
}
function Task(e, gf) {
const advanceToken = Symbol("advanceToken");
const doneToken = Symbol("doneToken");
let ret;
let done;
let task = {
join: function () {
if (done) {
done = false;
e.emit(doneToken, ret) // ???? TypeError: Generator is already running
return
}
e.on("task return", (data)=> {
done = false;
e.emit(DriveMainLoop, data)
})
}, // should return a future with a resolve as a callback
Sleep: Sleeper(e, advanceToken).Sleep,
advanceToken: advanceToken,
doneToken: doneToken,
setResult: function(result) {
e.emit("task return", result)
done = true
ret = result
}
}
let g = gf(task)
task.next = function(data) {
return g.next(data)
}
return task
}
function Kernel() {
let mainTask;
return {
Go: function (gf) {
let t = Task(e, gf);
e.on(t.doneToken, (data)=>{
mainTask.next(data);
});
e.on(t.advanceToken, (data)=> {
try {
let step = t.next(data);
if (step.done) {
t.setResult(step.value)
}
} catch (e) {
console.log("task err", e)
}
});
e.emit(t.advanceToken)
return t
},
Run: function (gf) {
mainTask = gf();
let g = mainTask;
e.on(DriveMainLoop, (data) => {
try {
let mainLoopResult = g.next(data);
} catch (e) {
console.log("main err", e)
}
});
e.emit(DriveMainLoop);
},
Sleep: Sleeper(e, DriveMainLoop).Sleep,
}
}
let kernel = Kernel();
kernel.Run(function* () {
let task = kernel.Go(function* (t) {
for (let i = 0; i < 2; i++) {
yield t.Sleep(200)
console.log("child job slept", i)
}
return "child job done!"
})
for (let i = 0; i < 5; i++) {
yield kernel.Sleep(200)
console.log("slept", i)
}
console.log("after for loop")
console.log("main loop", yield task.join()) // print child job done
});
console.log("blocked?"); // kernel Run does not block.
@CreatCodeBuild
Copy link
Author

I was trying to write this small library for teaching purpose. But I encountered a bug.

blocked?
child job slept 0
slept 0
child job slept 1
slept 1
slept 2
slept 3
slept 4
after for loop
main err TypeError: Generator is already running
    at Generator.next (<anonymous>)
    at MyEmitter.Go.e.on (D:\Projects\cspts\csp.mjs:59:26)
    at MyEmitter.emit (events.js:182:13)
    at Object.join (D:\Projects\cspts\csp.mjs:28:19)
    at D:\Projects\cspts\csp.mjs:105:41
    at Generator.next (<anonymous>)
    at MyEmitter.Run.e.on (D:\Projects\cspts\csp.mjs:80:44)
    at MyEmitter.emit (events.js:182:13)
    at Timeout._onTimeout (D:\Projects\cspts\csp.mjs:13:26)
    at ontimeout (timers.js:436:11)

The implementation isn't the best JS you can see but I believe the idea is clear.

@rchee
Copy link

rchee commented May 6, 2019

Just change line 92 in your code to name your Generator like below, I find that you call the main generator recursively. That is the problem.
If you call Generator.next in generator function itself, will throw TypeError: Generator is already running

-- kernel.Run(function* () {
++ kernel.Run(function * runGenerator() {

that is my log

blocked?
child job slept 0
slept 0
child job slept 1
slept 1
slept 2
slept 3
slept 4
after for loop
main err TypeError: Generator is already running
    at runGenerator.next (<anonymous>)    // <- where the recursive call happen
    at MyEmitter.Go.e.on (/Users/cheeqi/DEV/temp/js/csp.js:59:26)
    at emitOne (events.js:115:13)
    at MyEmitter.emit (events.js:210:7)
    at Object.join (/Users/cheeqi/DEV/temp/js/csp.js:28:19)
    at runGenerator (/Users/cheeqi/DEV/temp/js/csp.js:105:41)
    at runGenerator.next (<anonymous>)
    at MyEmitter.Run.e.on (/Users/cheeqi/DEV/temp/js/csp.js:80:44)
    at emitNone (events.js:105:13)
    at MyEmitter.emit (events.js:207:7)

@kanglinchan
Copy link

e.on(t.doneToken, (data)=>{
setImmediate(()=>{mainTask.next(data)})
});

@somarlyonks
Copy link

somarlyonks commented May 7, 2019

L58

-             e.on(t.doneToken, (data)=>{
-                 mainTask.next(data);
-             });
+             e.on(task.doneToken, data => setTimeout(() => mainTask.next(data)))

Ref:

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment