Maison  >  Article  >  interface Web  >  Analyser les problèmes avec le mécanisme middleware koa dans le nœud

Analyser les problèmes avec le mécanisme middleware koa dans le nœud

巴扎黑
巴扎黑original
2017-08-23 13:59:061249parcourir

Cet article présente principalement l'explication détaillée du mécanisme du middleware koa dans node, et présente en détail les problèmes de koa et de compatibilité. Ceux qui sont intéressés peuvent en savoir plus sur

. koa

koa est un framework Web plus petit, plus expressif et plus robuste créé par l'équipe originale d'express.

À mes yeux, koa est en effet beaucoup plus léger qu'express. koa me semble plus être un framework middleware. Koa n'est qu'une étagère de base. Lorsque vous avez besoin d'utiliser les fonctions correspondantes, utilisez-le simplement avec la correspondance. middleware, tel que le système de routage, etc. Un meilleur point est que l'express est traité en fonction des rappels. Quant à la qualité des rappels, vous pouvez rechercher et voir par vous-même. koa1 est basé sur la bibliothèque co, donc koa1 utilise Generator au lieu de rappels, et koa2 utilise async/await en raison de la prise en charge par le nœud de async/await. Concernant les bibliothèques async et co, vous pouvez vous référer à un article que j'ai écrit auparavant (Comprendre l'async). Koa peut être considéré comme une étagère pour divers middlewares. Jetons un coup d'œil à l'implémentation par koa de la partie middleware :

le middleware de koa1

koa1 est principalement utilisé. implémenté par Generator. De manière générale, un middleware de koa1 ressemble probablement à ceci :


app.use(function *(next){
  console.log(1);
  yield next;
  console.log(5);
});
app.use(function *(next){
  console.log(2);
  yield next;
  console.log(4);
});
app.use(function *(){
  console.log(3);
});

La sortie sera 1, 2, 3, 4, 5. du middleware de koa repose principalement sur koa-compose :


function compose(middleware){
 return function *(next){
  if (!next) next = noop();

  var i = middleware.length;
  // 组合中间件
  while (i--) {
   next = middleware[i].call(this, next);
  }

  return yield *next;
 }
}
function *noop(){}

Le code source est très simple, et la fonction implémentée est de combiner tous les middleware Pour concaténer les fichiers, passez d'abord un noop à l'avant-dernier middleware comme prochain, puis passez l'avant-dernier middleware trié comme à côté de l'avant-dernier middleware. Le dernier suivant est le premier middleware trié. C'est plus compliqué à dire, regardons ça en dessinant :

L'effet obtenu est tel que le montre l'image ci-dessus, qui est similaire à l'objectif que redux doit atteindre . Tant que le rendement suivant est rencontré, le prochain intermédiaire sera exécuté. Il est facile de connecter ce processus en série à l'aide de la bibliothèque co. Simulons brièvement l'implémentation complète du middleware :


.

const middlewares = [];

const getTestMiddWare = (loggerA, loggerB) => {
  return function *(next) {
    console.log(loggerA);
    yield next;
    console.log(loggerB);
  }
};
const mid1 = getTestMiddWare(1, 4),
  mid2 = getTestMiddWare(2, 3);

const getData = new Promise((resolve, reject) => {
  setTimeout(() => resolve('数据已经取出'), 1000);
});

function *response(next) {
  // 模拟异步读取数据库数据
  const data = yield getData;
  console.log(data);
}

middlewares.push(mid1, mid2, response);
// 简单模拟co库
function co(gen) {
  const ctx = this,
    args = Array.prototype.slice.call(arguments, 1);
  return new Promise((reslove, reject) => {
    if (typeof gen === 'function') gen = gen.apply(ctx, args);
    if (!gen || typeof gen.next !== 'function') return resolve(gen);

    const baseHandle = handle => res => {
      let ret;
      try {
        ret = gen[handle](res);
      } catch(e) {
        reject(e);
      }
      next(ret);
    };
    const onFulfilled = baseHandle('next'),
      onRejected = baseHandle('throw');
      
    onFulfilled();
    function next(ret) {
      if (ret.done) return reslove(ret.value);
      // 将yield的返回值转换为Proimse
      let value = null;
      if (typeof ret.value.then !== 'function') {
        value = co(ret.value);
      } else {
        value = ret.value;
      }
      if (value) return value.then(onFulfilled, onRejected);
      return onRejected(new TypeError('yield type error'));
    }
  });
}
// 调用方式
const gen = compose(middlewares);
co(gen);

middleware koa2

Avec la prise en charge par le nœud de async/await, il semble qu'il ne soit pas nécessaire de recourir à des bibliothèques d'outils comme co. ceux-ci directement, donc koa a également apporté des modifications. En regardant la situation actuelle, koa-compose :


function compose (middleware) {
 // 参数检验
 return function (context, next) {
  // last called middleware #
  let index = -1
  return dispatch(0)
  function dispatch (i) {
   if (i <= index) return Promise.reject(new Error(&#39;next() called multiple times&#39;))
   index = i
   let fn = middleware[i]
   // 最后一个中间件的调用
   if (i === middleware.length) fn = next
   if (!fn) return Promise.resolve()
   // 用Promise包裹中间件,方便await调用
   try {
    return Promise.resolve(fn(context, function next () {
     return dispatch(i + 1)
    }))
   } catch (err) {
    return Promise.reject(err)
   }
  }
 }
}

koa-compose utilise Promise. est passé de un à deux et le middleware suivant est exécuté. Il utilise wait next() Pour obtenir le même effet que l'exemple de code ci-dessus, vous devez modifier la façon dont le middleware est écrit :


const middlewares = [];
const getTestMiddWare = (loggerA, loggerB) => async (ctx, next) => {
  console.log(loggerA);
  await next();
  console.log(loggerB);
};

const mid1 = getTestMiddWare(1, 4),
  mid2 = getTestMiddWare(2, 3);
const response = async () => {
  // 模拟异步读取数据库数据
  const data = await getData();
  console.log(data);
};
const getData = () => new Promise((resolve, reject) => {
  setTimeout(() => resolve(&#39;数据已经取出&#39;), 1000);
});
middlewares.push(mid1, mid2);

// 调用方式
compose(middlewares)(null, response);

Comment faire Compatible

On voit que koa1 et koa2 ont encore de nombreuses différences dans la mise en œuvre du middleware si le middleware de koa1. est directement utilisé sous koa2, des erreurs se produiront certainement. Comment être compatible avec ces deux versions est également devenu un problème. L'équipe koa a écrit un package qui est le middleware de koa1 qui peut être utilisé dans koa2, appelé koa-convert. Voyons d'abord comment utiliser ce package :


function *mid3(next) {
  console.log(2, &#39;koa1的中间件&#39;);
  yield next;
  console.log(3, &#39;koa1的中间件&#39;);
}
convert.compose(mid3)
Jetons un coup d'œil à l'idée de mettre en œuvre ce package :


// 将参数转为数组,对每一个koa1的中间件执行convert操作
convert.compose = function (arr) {
 if (!Array.isArray(arr)) {
  arr = Array.from(arguments)
 }
 return compose(arr.map(convert))
}
// 关键在于convert的实现
const convert = mw => (ctx, next) => {
  // 借助co库,返回一个Promise,同时执行yield
  return co.call(ctx, mw.call(ctx, createGenerator(next)));
};

function * createGenerator (next) {
 /*
   next为koa-compomse中:
   function next () {
     return dispatch(i + 1)
   }
 */
 return yield next()
 // 执行完koa1的中间件,又回到了利用await执行koa2中间件的正轨
}
Personnellement, je pense que l'idée de koa-convert est d'encapsuler Generator Une couche de Promise permet d'appeler le middleware précédent en utilisant wait next() pour l'exécution de. Générateur, la bibliothèque co est utilisée pour assurer la compatibilité.

Ce qui précède est le contenu détaillé de. pour plus d'informations, suivez d'autres articles connexes sur le site Web de PHP en chinois!

Déclaration:
Le contenu de cet article est volontairement contribué par les internautes et les droits d'auteur appartiennent à l'auteur original. Ce site n'assume aucune responsabilité légale correspondante. Si vous trouvez un contenu suspecté de plagiat ou de contrefaçon, veuillez contacter admin@php.cn