50 строк серийного кода Обещание, как реализована луковая модель коа?

Node.js внешний интерфейс JavaScript
50 строк серийного кода Обещание, как реализована луковая модель коа?

1. Введение

Привет всем, яВакагава. Добро пожаловать, чтобы следовать за мнойПубличный аккаунт Wakagawa Vision, недавно организованныйЧтение исходного кода, если вам интересно, вы можете добавить меня в WeChatruochuan12Участвуйте в долгосрочном обмене и обучении.

написано раньше"Изучение серии "Общая архитектура исходного кода""ВключатьjQuery,underscore,lodash,vuex,sentry,axios,redux,koa,vue-devtools,vuex4Более десяти статей с исходным кодом. Два последних из них:

Выпущен Vue 3.2, как Ю Юйси выпустил Vue.js?

Практические основные функции инструментов в исходном коде Vue3, понятные новичкам.

Написание относительно сложного исходного кода стоило мне времени и энергии, и я не получил много лайков от чтения.На самом деле, это был настоящий удар. С точки зрения объема чтения и выгоды для читателей это не может способствовать непрерывному выпуску статей авторами.

Так что измените свое мнение и напишите несколько относительно простых для понимания статей.На самом деле исходный код не так сложен, как можно себе представить, по крайней мере многие люди могут понять.

Я уже писал статьи с исходным кодом koa.Изучите общую архитектуру исходного кода koa, проанализируйте принцип луковой модели koa и принцип совместной работы.Она относительно длинная, и читатели и друзья, скорее всего, не смогут ее закончить, поэтому эта статья начинается сkoa-compose50 строк исходного кода.

эта статья посвященарепозиторий koa-composeфайл, весьindex.jsХотя количество строк кода в файле меньше50ОК, и тестовый примерtest/test.jsдокумент300Это больше, чем линия, но этому стоит научиться.

Гёте однажды сказал: «Читать хорошую книгу — значит разговаривать с благородными людьми». То же самое можно получить: чтение исходного кода — это тоже способ обучения и общения с автором.

Прочитав эту статью, вы узнаете:

1. 熟悉 koa-compose 中间件源码、可以应对面试官相关问题
2. 学会使用测试用例调试源码
3. 学会 jest 部分用法

2. Подготовка окружающей среды

2.1 Клонировать проект koa-compose

Адрес склада этой статьи: koa-compose-analysis, спроситьstar~

# 可以直接克隆我的仓库,我的仓库保留的 compose 仓库的 git 记录
git clone https://github.com/lxchuan12/koa-compose-analysis.git
cd koa-compose/compose
npm i

Кстати: как мне сохранитьcomposeскладgitзаписано.

# 在 github 上新建一个仓库 `koa-compose-analysis` 克隆下来
git clone https://github.com/lxchuan12/koa-compose-analysis.git
cd koa-compose-analysis
git subtree add --prefix=compose https://github.com/koajs/compose.git main
# 这样就把 compose 文件夹克隆到自己的 git 仓库了。且保留的 git 记录

о большемgit subtree, вы можете увидеть эту статьюДвунаправленная синхронизация подпроектов между несколькими проектами Git с помощью Git Subtree с кратким руководством

Далее давайте посмотрим, как отлаживать исходный код в соответствии с тестовыми примерами, представленными в проекте с открытым исходным кодом.

2.2 Отладка исходного кода компоновки в соответствии с тестовым набором

использоватьVSCode(моя версия1.60), чтобы открыть проект и найтиcompose/package.json,оказатьсяscriptsа такжеtestЗаказ.

// compose/package.json
{
    "name": "koa-compose",
    // debug (调试)
    "scripts": {
        "eslint": "standard --fix .",
        "test": "jest"
    },
}

существуетscriptsТам должен бытьdebugили调试шрифт. нажмитеdebug(отладка), выберитеtest.

VSCode 调试

Затем будет выполнен тестовый примерtest/test.jsдокумент. Вывод терминала показан на изображении ниже.

koa-compose 测试用例输出结果

Затем мы отлаживаемcompose/test/test.jsдокумент. мы можем45行Нажмите точку останова и нажмите еще разpackage.json => srcipts => testВойдите в режим отладки. Как показано ниже.

koa-compose 调试

Затем нажмите кнопку выше, чтобы продолжить отладку. существуетcompose/index.jsСтавьте точки останова в ключевых местах файла, а отлаживайте и изучайте исходный код с вдвое меньшими усилиями.

Для получения дополнительной информации об отладке nodejs вы можете просмотреть официальную документацию.

Кстати, несколько кнопок, связанных с отладкой, подробно описаны.

    1. Продолжить (F5): После нажатия код будет выполнен непосредственно до следующей точки останова, если следующей точки останова нет, считается, что выполнение текущего кода завершено.
    1. Пропуск одного шага (F10): после нажатия выполняется переход к следующей строке текущего кода для продолжения выполнения, и функция не запускается.
    1. Пошаговая отладка (F11): нажмите, чтобы войти во внутреннюю отладку текущей функции, например, вcomposeВыполните пошаговую отладку в этой строке, и вы войдете вcomposeОтладка внутри функции.
    1. Выход (Shift + F11): нажмите, чтобы выйти из текущей отлаживаемой функции, что соответствует одноэтапной отладке.
    1. Перезагрузите (Ctrl + Shift + F5): как следует из названия.
    1. Разорвать ссылку (Shift + F5): как следует из названия.

Далее мы следуем тестовому примеру, чтобы изучить исходный код.

3. Следуйте тестовому примеру, чтобы изучить исходный код

Поделитесь советом по тест-кейсу: мы можем добавить тест-кейсonlyретушь.

// 例如
it.only('should work', async () => {})

Таким образом, мы можем просто выполнить текущий тестовый пример и не заботиться о других, не мешая отладке.

3.1 Нормальный процесс

Открытьcompose/test/test.jsфайл, см. первый тестовый пример.

// compose/test/test.js
'use strict'

/* eslint-env jest */

const compose = require('..')
const assert = require('assert')

function wait (ms) {
  return new Promise((resolve) => setTimeout(resolve, ms || 1))
}
// 分组
describe('Koa Compose', function () {
  it.only('should work', async () => {
    const arr = []
    const stack = []

    stack.push(async (context, next) => {
      arr.push(1)
      await wait(1)
      await next()
      await wait(1)
      arr.push(6)
    })

    stack.push(async (context, next) => {
      arr.push(2)
      await wait(1)
      await next()
      await wait(1)
      arr.push(5)
    })

    stack.push(async (context, next) => {
      arr.push(3)
      await wait(1)
      await next()
      await wait(1)
      arr.push(4)
    })

    await compose(stack)({})
    // 最后输出数组是 [1,2,3,4,5,6]
    expect(arr).toEqual(expect.arrayContaining([1, 2, 3, 4, 5, 6]))
  })
}

Прочитав этот тестовый пример,contextчто это такое,nextЧто это такое.

существуетkoaдокументацияСуществует очень репрезентативное промежуточное ПО наgifкартина.

中间件 gif 图

а такжеcomposeФункция функции состоит в том, чтобы добавить функцию, добавленную в массив промежуточного программного обеспечения в соответствии с приведенным вышеgifПоследовательное выполнение графов.

3.1.1 функция создания

Проще говоря,composeФункция в основном делает две вещи.

    1. Принимает один параметр, параметр проверки — это массив, а каждый элемент в массиве проверки — это функция.
    1. Возвращает функцию, которая принимает два параметра, а именноcontextа такжеnext, эта функция, наконец, возвращаетPromise.
/**
 * Compose `middleware` returning
 * a fully valid middleware comprised
 * of all those which are passed.
 *
 * @param {Array} middleware
 * @return {Function}
 * @api public
 */
function compose (middleware) {
  // 校验传入的参数是数组,校验数组中每一项是函数
  if (!Array.isArray(middleware)) throw new TypeError('Middleware stack must be an array!')
  for (const fn of middleware) {
    if (typeof fn !== 'function') throw new TypeError('Middleware must be composed of functions!')
  }

  /**
   * @param {Object} context
   * @return {Promise}
   * @api public
   */

  return function (context, next) {
    // last called middleware #
    let index = -1
    return dispatch(0)
    function dispatch(i){
      // 省略,下文讲述
    }
  }
}

Давайте посмотримdispatchфункция.

3.1.2 диспетчерская функция

function dispatch (i) {
  // 一个函数中多次调用报错
  // await next()
  // await next()
  if (i <= index) return Promise.reject(new Error('next() called multiple times'))
  index = i
  // 取出数组里的 fn1, fn2, fn3...
  let fn = middleware[i]
  // 最后 相等,next 为 undefined
  if (i === middleware.length) fn = next
  // 直接返回 Promise.resolve()
  if (!fn) return Promise.resolve()
  try {
    return Promise.resolve(fn(context, dispatch.bind(null, i + 1)))
  } catch (err) {
    return Promise.reject(err)
  }
}

Стоит отметить, что:bindФункция — это функция, которая возвращает новый. Первый параметр — это указатель this в функции (если функции не нужно использоватьthis, обычно записывается какnull). это предложениеfn(context, dispatch.bind(null, i + 1),i + 1это дляlet fn = middleware[i]Выбиратьmiddlewareследующая функция в . то естьnextэто функция в следующем промежуточном программном обеспечении. также объясните вышеgifПорядок выполнения функции графа. Окончательный порядок массива в тестовом примере:[1,2,3,4,5,6].

3.1.3 Упрощение создания для облегчения понимания

После самостоятельной отладки вы обнаружитеcomposeПосле выполнения это структура, подобная этой (опуститьtry catchсудить).

// 这样就可能更好理解了。
// simpleKoaCompose
const [fn1, fn2, fn3] = stack;
const fnMiddleware = function(context){
    return Promise.resolve(
      fn1(context, function next(){
        return Promise.resolve(
          fn2(context, function next(){
              return Promise.resolve(
                  fn3(context, function next(){
                    return Promise.resolve();
                  })
              )
          })
        )
    })
  );
};

то естьkoa-composeвозвращаетPromise,от中间件(传入的数组)Вынесите первую функцию изcontextи первыйnextфункция для выполнения.
ПервыйnextФункция также возвращаетPromise,от中间件(传入的数组)Уберите вторую функцию изcontextи второйnextфункция для выполнения.
второйnextФункция также возвращаетPromise,от中间件(传入的数组)Вычеркните третью функцию изcontextи третийnextфункция для выполнения.
Третий...
И так далее. В последнем мидлваре есть вызовnextфункция, она возвращаетPromise.resolve. Если нет, не выполнятьnextфункция.

Это соединяет все промежуточное ПО последовательно. Это то, что мы часто называем моделью лука.

洋葱模型图如下图所示:

Я должен сказать, что это очень удивительно, «играть все равно великий бог будет играть».

3.2 Перехват ошибок

it('should catch downstream errors', async () => {
  const arr = []
  const stack = []

  stack.push(async (ctx, next) => {
    arr.push(1)
    try {
      arr.push(6)
      await next()
      arr.push(7)
    } catch (err) {
      arr.push(2)
    }
    arr.push(3)
  })

  stack.push(async (ctx, next) => {
    arr.push(4)
    throw new Error()
  })

  await compose(stack)({})
  // 输出顺序 是 [ 1, 6, 4, 2, 3 ]
  expect(arr).toEqual([1, 6, 4, 2, 3])
})

Поверьте, чтобы понять первый тестовый пример иcomposeфункция, также лучше понять этот тестовый пример. Эта часть на самом деле является соответствующим кодом здесь.

try {
    return Promise.resolve(fn(context, dispatch.bind(null, i + 1)))
} catch (err) {
  return Promise.reject(err)
}

3.3 Следующая функция не может быть вызвана несколько раз

it('should throw if next() is called multiple times', () => {
  return compose([
    async (ctx, next) => {
      await next()
      await next()
    }
  ])({}).then(() => {
    throw new Error('boom')
  }, (err) => {
    assert(/multiple times/.test(err.message))
  })
})

Этому соответствует:

index = -1
dispatch(0)
function dispatch (i) {
  if (i <= index) return Promise.reject(new Error('next() called multiple times'))
  index = i
}

после двойного звонкаiа такжеindexкак для1, поэтому будет сообщено об ошибке.

compose/test/test.jsВ файле более 300 строк, и есть много тестовых случаев, которые можно отлаживать самостоятельно по методам, описанным в тексте.

4. Резюме

несмотря на то чтоkoa-composeИсходный код составляет менее 50 строк, но если вы впервые видите исходный код для отладки исходного кода, это все равно будет сложно. Он смешивается с функциями высшего порядка, замыканиями,Promise,bindи другие базовые знания.

Благодаря этой статье мы познакомились сkoa-composeЛуковая модель, о которой часто говорит промежуточное ПО, я изучил ее частьjestиспользования, а также научились использовать готовые тест-кейсы для отладки исходного кода.

Я считаю, что после того, как вы научитесь отлаживать исходный код с помощью тестовых примеров, вы почувствуете, что исходный код не так сложен, как вы думаете..

Проекты с открытым исходным кодом обычно имеют очень подробные тестовые примеры. Помимо удобства в изучении исходного кода и отладки исходного кода, это также может вдохновить нас: мы также можем постепенно внедрять инструменты тестирования для проектов в нашу собственную работу, такие какjest.

Кроме того, чтение исходного кода проектов с открытым исходным кодом является для нас лучшим способом изучить дизайнерские идеи и реализацию исходного кода крупных коров отрасли.

Прочитав эту статью, я очень надеюсь, что смогу попрактиковаться в отладке исходного кода, чтобы научиться самому, который легко усваивается и переваривается. Кроме того, если у вас есть лишняя энергия, вы можете продолжать смотреть мойkoa-composeИсходная статья:Изучите общую архитектуру исходного кода koa, проанализируйте принцип луковой модели koa и принцип совместной работы.

Наконец, добро пожаловать, чтобы добавить меня на WeChatruochuan12общаться, участвоватьЧитайте исходный код вместеДействия, все вместе изучают исходный код и вместе добиваются прогресса.


О группе && Exchange

недавно организованныйЧтение исходного кода, если вам интересно, вы можете добавить меня в WeChatruochuan12Участвуйте в долгосрочном обмене и обучении.

Автор: Чанг ИВакагаваНазвание смешано в реках и озерах. добро пожаловать, чтобы добавить меня wechatruochuan12. По дороге на фронт | Знаю очень мало, только хорошо учусь.
Обратите внимание на паблик аккаунта Ruochuan Vision, Изучайте исходный код вместе каждую неделю, учитесь читать исходный код и переходите к расширенному интерфейсу.
Блог Вакагавы
segmentfaultКолонна Вакагава Видение, открылВидение ВакагаваКолонка, добро пожаловать на внимание ~
Колонка самородков, добро пожаловать, обратите внимание~
Колонна видений Чжиху Руочуань, открылВидение ВакагаваКолонка, добро пожаловать на внимание ~
github blog, спроситьstar^_^~