I can't find any good documentation (or any question on SO) that explains how exactly yield
and run
works.
I am not able to find how would an asynchronous method will be able to return a value using Fibers/futures
.
For example (code not syntactically correct), how can I make this function return the response
synchronously
function findData( param )
{
var fiber = Fiber( function(){
var currentFiber = Fiber.current;
Model.findOne({ "param" : param}, function (err, data) {
response = { err : err, data : data };
});
});
return fiber;
}
Something like
var value = findData("1");
This Model
is an object that I get from Mongoose
schema class (not sure if it is relevant).
Thanks in advance.
Node fibers make it possible to suspend the running of any function by saving the state of the current executing environment in a platform dependent way at the lowest level (For example windows has a fiber concept, not widely used, more lightweight than a thread, not preemptive).
All other js libraries implement co-routine continuation by using callback functions, storing the execution state in scope variables. This means you either have callback pyramid, a promise chain, or async/await (I put decorated generators in the same bucket as async/await).
Fibers are also a possible implementation of co-routines. Fibers should be fast, and integrating them in your code does not require you to write in a different codestyle, or introducing new syntax. Execution contexts (stack, registers, etc...) which can be changed to and from at will, from your own code.
This cannot be done in pure JavaScript, node-fibers use native libraries to achieve this!
The node-fibers specific concept is: the javascript event loop is outside of all fibers, thus your initial code runs without fibers too. If you have a fiber reference, you can pass the right to run to it by fiber.run();
. When you are inside a fiber, you can give up the right to run by calling Fiber.yield();
(effectively suspending the currently running code), and the javascript event loop will continue. All builtin callbacks (setTimeout
, Promise.then
, event handlers, http request callbacks) will run in the javascript event loop, without a fiber.
const Fiber = require("fibers");
function findDataAsync(param, callback) {
setTimeout(() => {
callback(null, "Async returned data");
}, 100);
}
function findData( param ) {
const currentFiber = Fiber.current;
var response = null;
findDataAsync(param, function (err, data) {
response = { err : err, data : data };
currentFiber.run();
});
Fiber.yield();
if (response.err) {
throw response.err;
} else {
return response.data;
}
}
function main() {
console.log("Inside fiber started");
console.log(findData());
console.log("Inside fiber finished");
}
console.log("Outside fiber started");
Fiber(main).run();
console.log("Outside fiber finished");
This should output:
Outside fiber started
Inside fiber started
Outside fiber finished
Async returned data
Inside fiber finished
Notice that Outside fiber finished
is logged immediately after the first yield in the fiber is called.
As you see, we had to start a fiber immediately to be able to yield
. If you try to use fibers in a third party library, you have to make sure that the library does not "reset" your current execution context to the javascript event loop by calling setTimeout
or issuing asynchronous http requests.