[Узел] Объясните модель лука Коа простыми словами

внешний интерфейс JavaScript опрос
[Узел] Объясните модель лука Коа простыми словами

Эта статья объяснитkoaМодель лука, почему мы должны использовать модель лука и внедрить ее принципы. Оставьте модель лука для пониманияkoaЭто очень важно, надеюсь, эта статья поможет вам~

Какая модель лука

давайте посмотрим одинdemo

const Koa = require('koa');
const app = new Koa();

// 中间件1
app.use((ctx, next) => {
    console.log(1);
    next();
    console.log(2);
});

// 中间件 2 
app.use((ctx, next) => {
    console.log(3);
    next();
    console.log(4);
});

app.listen(8000, '0.0.0.0', () => {
    console.log(`Server is starting`);
});

Результат:

1
3
4
2

существуетkoaпромежуточное ПОnext()Метод разделен на две части.next()Верхняя часть метода будет выполняться первой, а нижняя часть будет выполняться после того, как будут завершены все последующие выполнения промежуточного программного обеспечения. Это хорошо видно из следующего рисунка:

В луковой модели каждый уровень эквивалентен промежуточному программному обеспечению, которое выполняет определенные функции, такие как обработка ошибок,Sessionобработка и так далее. Порядок обработки первыйnext()предварительный запрос (Request, от внешнего слоя к внутреннему слою), а затем выполнитьnext()функцию и, наконец,next()пост-ответ (Response, от внутреннего к внешнему), т.Каждое промежуточное ПО имеет две возможности обработки.

Почему Koa использует модель onion

Если это не луковичная модель, что нам делать, если наше промежуточное ПО зависит от логики другого промежуточного ПО?

Например, нам нужно знать запрос или операциюdbСколько времени это займет, и хотите получить информацию о другом промежуточном программном обеспечении. существуетkoa, мы можем использоватьasync awaitспособ в сочетании с моделью лука, чтобы сделать это.

app.use(async(ctx, next) => {
  const start = new Date();
  await next();
  const delta = new Date() - start;
  console.log (`请求耗时: ${delta} MS`);
  console.log('拿到上一次请求的结果:', ctx.state.baiduHTML);
})

app.use(async(ctx, next) => {
  // 处理 db 或者进行 HTTP 请求
  ctx.state.baiduHTML = await axios.get('http://baidu.com');
})

И это было бы невозможно без модели лука.

Погрузитесь в модель лука Коа

Начнем со статьиdemoанализироватьkoaВнутренняя реализация.

const Koa = require('koa');

//Applications
const app = new Koa();

// 中间件1
app.use((ctx, next) => {
  console.log(1);
  next();
  console.log(2);
});

// 中间件 2 
app.use((ctx, next) => {
  console.log(3);
  next();
  console.log(4);
});

app.listen(9000, '0.0.0.0', () => {
    console.log(`Server is starting`);
});

использовать метод

useСпособ состоит в том, чтобы делать одну вещь и поддерживать ееmiddlewareмассив промежуточного ПО

  use(fn) {
    // ...
    // 维护中间件数组——middleware
    this.middleware.push(fn);
    return this;
  }

метод прослушивания и метод обратного вызова

воплощать в жизньapp.listenметод, по сутиNode.jsРоднойhttpмодульcreateServerметод создает службу, обратный вызов которойcallbackметод.callbackМетод находится в центре нашего внимания сегодняcomposeфункция, возвращающая значениеPromiseфункция.

  listen(...args) {
    debug('listen');
    // node http 创建一个服务
    const server = http.createServer(this.callback());
    return server.listen(...args);
  }

  callback() {
    // 返回值是一个函数
    const fn = compose(this.middleware);
    const handleRequest = (req, res) => {
      // 创建 ctx 上下文环境
      const ctx = this.createContext(req, res);
      return this.handleRequest(ctx, fn);
    };
    return handleRequest;
  }

handleRequestбудет казненcomposeвозвращается функциейPromiseфункцию и вернуть результат.

  handleRequest(ctx, fnMiddleware) {
    const res = ctx.res;
    res.statusCode = 404;
    const onerror = err => ctx.onerror(err);
    const handleResponse = () => respond(ctx);
    onFinished(res, onerror);
    // 执行 compose 中返回的函数,将结果返回
    return fnMiddleware(ctx).then(handleResponse).catch(onerror);
  }

koa-compose

composeСсылка на функциюkoa-composeэта библиотека. Его реализация выглядит так:

function compose (middleware) {
  // ...
  return function (context, next) {
    // last called middleware #
    let index = -1
    // 一开始的时候传入为 0,后续会递增
    return dispatch(0)
    function dispatch (i) {
      // 假如没有递增,则说明执行了多次
      if (i <= index) return Promise.reject(new Error('next() called multiple times'))
      index = i
      // 拿到当前的中间件
      let fn = middleware[i]
      if (i === middleware.length) fn = next
      // 当 fn 为空的时候,就会开始执行 next() 后面部分的代码
      if (!fn) return Promise.resolve()
      try {
        // 执行中间件,留意这两个参数,都是中间件的传参,第一个是上下文,第二个是 next 函数
        // 也就是说执行 next 的时候也就是调用 dispatch 函数的时候
        return Promise.resolve(fn(context, dispatch.bind(null, i + 1)));
      } catch (err) {
        return Promise.reject(err)
      }
    }
  }
}

Код очень простой, давайте посмотрим на конкретный поток выполнения:

Когда мы выполняем в первый раз, вызовdispatch(0), на этот раз я 0,fnэто первая промежуточная функция. И выполнить мидлвар, обратите внимание на эти два параметра, которые являются параметрами мидлвара, первый это контекст, второй этоnextфункция. то естьКогда промежуточное ПО выполняется следующим, это также вызывает функцию диспетчеризации., поэтому выполнениеnextПричина, по которой следующее промежуточное ПО будет выполнено, когда логика:

return Promise.resolve(fn(context, dispatch.bind(null, i + 1)));

При втором и третьем исполненииdispatchКогда , как и в первый раз, начнем выполнять второе и третье промежуточное программное обеспечение соответственно, выполнитеnext()начинает выполнение следующего промежуточного программного обеспечения.

Когда выполняется третье промежуточное программное обеспечение, оно выполняется дляnext()когда,dispatchВ функцию передается параметр 3,fnдляundefined. В это время он будет выполнен

if (!fn) return Promise.resolve()

В это время будет выполнено третье промежуточное ПО.next()Код после этого, затем второй, первый, формируя таким образом модель лука.

Процесс выглядит следующим образом:

Простая версия компоновки

модельkoaлогики, мы можем написать упрощенную версиюcompose. Чтобы облегчить ваше понимание:

const middleware = []
let mw1 = async function (ctx, next) {
    console.log("next前,第一个中间件")
    await next()
    console.log("next后,第一个中间件")
}
let mw2 = async function (ctx, next) {
    console.log("next前,第二个中间件")
    await next()
    console.log("next后,第二个中间件")
}
let mw3 = async function (ctx, next) {
    console.log("第三个中间件,没有next了")
}

function use(mw) {
  middleware.push(mw);
}

function compose(middleware) {
  return (ctx, next) => {
    return dispatch(0);
    function dispatch(i) {
      const fn = middleware[i];
      if (!fn) return;
      return fn(ctx, dispatch.bind(null, i+1));
    }
  }
}

use(mw1);
use(mw2);
use(mw3);

const fn = compose(middleware);

fn();

Суммировать

KoaЛуковая модель относится кnext()Функция является точкой разделения и сначала выполняется снаружи внутрь.Requestлогика, а затем выполнять изнутри наружуResponseлогика. Благодаря луковой модели становится более осуществимой и простой связь между несколькими промежуточными программами и так далее. Принцип его реализации не очень сложен, в основномcomposeметод.

Ссылаться на