[Node. js] Generator and koa Analysis in ECMAScript 6
I 've heard from you that koa is a good method. I 've also caught up with it in the past two days: I installed node 0.11.12 with n, and I got a koa to start the harmony mode and try it out. In a series of documents and posts, we probably realized that koa is a new-generation Web framework dominated by corner stone. koa's middleware is based on ES6 generator functions (function *) the core process library of koa is co, which can solve the Pyramid of Doom problem. koa is in contact with Node. before js, I had Python programming experience, and I knew exactly what the generator was. What I'm really interested in is how it is used to optimize callback nesting. Why is there such a piece in the KOA framework (segment 1): copy the code var fs = require ('fs '); var app = require ('koa') (); var readFile = function (dir) {return function (fn) {fs. readFile (dir, fn) ;}} app. use (function * () {var arr = yield defaults '1.txt ', '2.txt', '3.txt ']. map (function (path) {return readFile (path) ;}); this. body = arr. join (',');}) app. listen (8000); copy the Code. This Code demonstrates how koa uses the constructor of the generator function (function * () and function. name = 'generato RFunction ') to serialize asynchronous callback. Its Execution Process is: function *(){...} as a generator function, koa is pushed to the middleware queue of koa to use the co framework to call and execute the generator function. The execution of the generator function does not immediately execute the function body, but generates the generator (generator) instance -- at the same time, the generator can be regarded as an instance that complies with the iterator protocol. Each time next () of the iterator is called, a {value: obj, done: true/false} object will be returned: value is the execution result value. done indicates whether the next () method of the generator instance is called after the iteration is completed. The execution flow within the function body is started until yield occurs, the next () will return the execution result value to yield and suspend it to the next yield appearance. It can be understood that yield always returns the last next () if next () has a parameter, yield will return this parameter value (asynchronous callback has the opportunity to inject execution results) to observe the above code snippet I We noticed that the fn code of the callback handler function of readFile is not provided, so how does koa or co process this callback function? In the co Source Code (line: 84) that comes with koa (0.6.2), you can see the following snippet (Segment 2): copy the code // normalizeret. value = toThunk (ret. value, ctx); // runif ('function' = typeof ret. value) {var called = false; try {ret. value. call (ctx, function () {if (called) return; called = true; next. apply (ctx, arguments) ;});} catch (e) {setImmediate (function () {if (called) return; called = true; next (e) ;}}return ;}copy the code toThunk and convert it to a standard function (segment 3): copy the code function toThunk (obj, ctx) {if (isGeneratorFunction (obj) {return co (obj. call (ctx);} if (isGenerator (obj) {return co (obj);} if (isPromise (obj) {return promiseToThunk (obj );} if ('function' = typeof obj) {return obj;} if (isObject (obj) | Array. isArray (obj) {return objectToThunk. call (ctx, obj);} return obj;} copy the code in Part 1. The generator function first returns a generator. Then, yield and map return three function objects, that is, the high-order function readFi Function returned by le: function (fn) {fs. readFile (dir, fn);} Segment 2 calls the function based on the Type returned by segment 3, and provides a callback function: pass arguments through next. apply (ctx, arguments); cleverly passed. As mentioned above, if the next () parameter is provided, yield obtains the result value, which is the result of the callback. Who are you looking at? If you haven't gotten dizzy after reading the above paragraphs, it's of course your best! :)-my presentation skills are really not enough to clearly explain the mystery of the framework, but in my opinion, what's really embarrassing is the ES6 Generator mechanism itself. Temporarily put down the co framework and slightly modify part 1 (Part 4): copy the code var fs = require ('fs'); var path = require ('path '); var readFile = function (dir) {return function (fn) {fs. readFile (dir, {encoding: 'utf8', flag: 'R'}, fn) ;};}; function * readFileGeneratorFunction (path, cb) {console. log (yield readFile (path) (cb);} var readFileIterator = readFileGeneratorFunction ('testdate. js', callback); function callback (err, data) {readfileiworkflow Tor. next (data);} readFileIterator. next (); the intention to copy the code is obvious: This readFileGeneratorFunction is a generator function. Execute it to return the function returned by a generator (iterator) high-level function, when the callback next is specified during the execution of the generator function to trigger the execution of the callback, the yield issue is also obvious when next (data) carries the result value, and the Business Code (yield in GeneratorFunction) it needs to be prefixed to process control (callback), which is not scientific. Abstract: an execution function of a generator function can be provided: copy the code function run (generatorFunction) {var generatorItr = generatorFunction (callback); function callback (err, data) {if (err) console. log (err); generatorItr. next (data);} generatorItr. run (function * rfGenFunc (cb) {console. log ('first'); console. log (yield readFile('1.txt ') (cb); console. log ('second'); console. log (yield readFile('2.txt ') (cb ));});