抱歉,您的浏览器无法访问本站

本页面需要浏览器支持(启用)JavaScript


了解详情 >

最近好久没更新了,工作太忙 + 感冒,anyway 总有不更新的理由。。。

最近喜欢读源码了,不得不说,读源码可以快速的把要用到的框架/库,一次性的吃透,而且比看文档理解的更透彻、更踏实!

当然我也没有大佬那种魄力,读什么 Vue, React 这种大型框架,仅仅简单瞅瞅,不过多深入,用到哪里看到哪里。

今天先从 koa2 以前要用到的转换 Generator function 的工具函数:convert 看起吧!

Koa-convert:koajs/convert: Convert koa generator-based middleware to promise-based middleware (github.com)

co:tj/co: The ultimate generator based flow-control goodness for nodejs (supports thunks, promises, etc) (github.com)

解决什么问题?

在node 7.6版本以下,是不支持async function的,但如果你想在koa里优雅的处理当前任务需要上一个异步任务的完成的情况时,通常是使用Generator function(当然你也可以配置一些 polyfill)

Usage

const Koa = require('koa') // koa v2.x
const convert = require('koa-convert')
const app = new Koa()

app.use(modernMiddleware)

app.use(convert(legacyMiddleware))

function * legacyMiddleware (next) {
  // before
  yield next
  // after
}

function modernMiddleware (ctx, next) {
  // before
  return next().then(() => {
    // after
  })
}

在以前(koa v1.x)的时候,使用 convert来转换 Generator function

源码

convert Convert legacy generator middleware to modern promise middleware.

传统生成器式中间件转换成现代 Promise 式中间件


/**
 * Convert Koa legacy generator-based middleware
 * to modern promise-based middleware.
 *
 *
 * @api public
 * */

function convert (mw) {
  // mw 必须是函数
  if (typeof mw !== 'function') {
    throw new TypeError('middleware must be a function')
  }

  // assume it's Promise-based middleware
  // 普通函数可以直接返回了
  if (
    mw.constructor.name !== 'GeneratorFunction' &&
    mw.constructor.name !== 'AsyncGeneratorFunction'
  ) {
    return mw
  }
	
  // 实际执行的中间件
  const converted = function (ctx, next) {
    // co 是一个 async 语法糖实现
    return co.call(
      ctx,
      // 传入的 mw 是 Generator function 执行后返回 Generator Object
      mw.call(
        ctx,
        (function * (next) { return yield next() })(next)
      ))
  }

  converted._name = mw._name || mw.name
  return converted
}

短短几行代码而已,co 调用时会传入 ctx ,这个上下文对象是会真正的传入 待转换Generator function的,作为 this 指针,指向 ctx。而 mw.call(ctx, ...) 传入的 ctx 在第一次执行 Generator function的时候绑定到 this 指针上。但这一特性对箭头函数无效,koa2 更提倡使用箭头函数

签名已更改为通过 ctx 取代 this 显式参数传递 Context

上下文传递更改使得 koa 更能兼容 es6 的箭头函数,通过捕获 “this”。

—— koa2 中文文档

convert.back Convert modern promise middleware back to legacy generator middleware.

现代 Promise 式中间件 转回成传统生成器式中间件


/**
 * Convert Koa modern promise-based middleware
 * to legacy generator-based middleware.
 *
 *
 * @api public
 * */

convert.back = function (mw) {
  if (typeof mw !== 'function') {
    throw new TypeError('middleware must be a function')
  }

  // assume it's generator middleware
  // 如果已经是生成器函数则不必转换
  if (mw.constructor.name === 'GeneratorFunction' 
      || mw.constructor.name === 'AsyncGeneratorFunction') {
    return mw
  }
	
  // 实际转换成的函数
  const converted = function * (next) {
    const ctx = this
    let called = false

    yield mw(ctx, function () {
      if (called) {
        // guard against multiple next() calls
        throw new Error('next() called multiple times')
      }

      called = true

      /* 如果返回值不是普通变量
      *  co 将会一直执行下去
      */
      return co.call(ctx, next)
    })
  }

  converted._name = mw._name || mw.name
  return converted
}

convert.backconvert的逆运算,不明白有何意义?

总结

本来以为简简单单的几行代码,实际写完还是有些许不理解,先记录下来,有新的收获再谈

Q1:convert返回的函数中,为何绑定两次this,给mw绑定的ctx什么时候会用到,有实际意义吗?

Q2:convert.back返回的函数中,为什么要判断是否 called ? next 执行两次在什么情况会触发?

评论