koa源码中promise的解读(2)

function convert (mw) { // mw为generator中间件 if (typeof mw !== 'function') { throw new TypeError('middleware must be a function') } if (mw.constructor.name !== 'GeneratorFunction') { // assume it's Promise-based middleware return mw } const converted = function (ctx, next) { return co.call(ctx, mw.call(ctx, createGenerator(next))) } converted._name = mw._name || mw.name return converted } function * createGenerator (next) { return yield next() }

从上面代码可以看出,koa-convert 在 generator 外部包裹了一个函数来提供与其他中间件一致的接口,内部利用 co 模块来执行 generator 函数,这里我想聊的就是 co 模块的原理,generator 函数执行时并不会立即执行其内部逻辑,而是返回一个遍历器对象,然后通过调用该遍历器对象的 next 方法来执行,generator 函数本质来说是一个状态机,如果内部有多个 yield 表达式,就需要 next 方法执行多次才能完成函数体的执行,而 co 模块的能力就是实现 generator 函数的 自动执行,不需要手动多次调用 next 方法,那么它是如何做到的呢?co 源码如下:

function co(gen) { var ctx = this; var args = slice.call(arguments, 1); // we wrap everything in a promise to avoid promise chaining, // which leads to memory leak errors. // see https://github.com/tj/co/issues/180 return new Promise(function(resolve, reject) { if (typeof gen === "function") gen = gen.apply(ctx, args); if (!gen || typeof gen.next !== "function") return resolve(gen); onFulfilled(); /** * @param {Mixed} res * @return {Promise} * @api private */ function onFulfilled(res) { var ret; try { ret = gen.next(res); } catch (e) { return reject(e); } next(ret); } /** * @param {Error} err * @return {Promise} * @api private */ function onRejected(err) { var ret; try { ret = gen.throw(err); } catch (e) { return reject(e); } next(ret); } /** * Get the next value in the generator, * return a promise. * * @param {Object} ret * @return {Promise} * @api private */ function next(ret) { if (ret.done) return resolve(ret.value); // toPromise是一个函数,返回一个promise示例 var value = toPromise.call(ctx, ret.value); if (value && isPromise(value)) return value.then(onFulfilled, onRejected); return onRejected( new TypeError( "You may only yield a function, promise, generator, array, or object, " + 'but the following object was passed: "' + String(ret.value) + '"' ) ); } }); }

从 co 源码来看,它先是手动执行了一次onFulfilled 函数来触发 generator 遍历器对象的 next 方法,然后利用promise的onFulfilled 函数去自动完成剩余状态机的执行,在onRejected 中利用遍历器对象的 throw 方法抛出执行上一次 yield 过程中遇到的异常,整个实现过程可以说是相当简洁优雅。

结语

通过上面的例子可以看出 promise 的能量是非常强大的,koa 的中间件实现和 co 模块的实现都是基于 promise,除了应用于日常的异步流程控制,在开发过程中我们还可以大大挖掘其潜力,帮助我们完成一些自动化程序工作流的事情。

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:http://www.heiqu.com/36116d334f3ccdaa5bbe0efd82c6e59d.html