It has to be rather simple, but I can't understand the solution for the beginning exercise from koa workshop.
The test:
var co = require('co');
var assert = require('assert');
var fs = require('./index.js');
describe('.stats()', function () {
it('should stat this file', co(function* () {
var stats = yield fs.stat(__filename);
assert.ok(stats.size);
}));
});
The solution and the task:
var fs = require('fs');
/**
* Create a yieldable version of `fs.stat()`:
*
* app.use(function* () {
* var stats = yield exports.stat(__filename);
* })
*
* Hint: you can return a yieldable.
*/
exports.stat = function (filename) {
return function (done) {
fs.stat(filename, done);
}
};
The way I think of this test is: co
library runs the generator function for us, the fs.stat(__filename)
invokes, returns the
function (done) {
fs.stat(filename, done);
}
Then, all I have are questions: why does anonymous function returns fs.stat()
at the same place and where does it take done
callback? I have logged this callback out, it's generators next()
method with stats
object as a passing parameter, but I can't find any information about callbacks injection in co
. How does this work? Thank you in advance.
I wasn't able to find this information on the main README.md
, but it looks like co
provides a callback to thunks automatically. So in the example above co
provides the done
callback and invokes the:
function (done) {
fs.stat(filename, done);
}
If there is an error this callback expects that the main function would return error (done(err);
), if everything is ok: done(null, result);
. After that the result is passed to generator. Here is the done
callback code:
function () {
if (called) return;
called = true;
next.apply(ctx, arguments);
}
Well, let's return to the solution. The co
's git README.md
says:
Thunk support only remains for backwards compatibility and may be removed in future versions of
co
.
So let's rewrite it to the modern view with promises:
exports.stat = function(filename) {
return new Promise((resolve, reject) => {
fs.stat(filename, (err, stats) => {
if (err) {reject(err);}
resolve(stats);
});
});
};
We wrap the promise with the anonymous function which get the filename
value and encapsulates it for the returning promise object, which is one of the supported yieldables in co
. This promise starts the fs.stat
with the callback. This callback resolve
s the promise if everything is ok, or reject
s it otherwise. The resolve
d result returns into the generator.