За многими общими темами в области переднего плана на самом деле стоят классические основы компьютерных наук. Сегодня, пока вы используете JS для инициирования сетевых запросов, вы в основном используете монады в функциональном программировании. так что случилось? Начнем с ада обратного вызова...
Callback Hell и обещания
Студенты, знакомые с JS, не будут незнакомы с функциями обратного вызова — это наиболее распространенный способ обработки асинхронных событий в этом языке. Однако, как мы знаем,Последовательная обработка нескольких асинхронных задачРабочий процесс может легко вызвать вложение обратных вызовов, заставляя код трудно поддерживать:
$.get(a, (b) => {
$.get(b, (c) => {
$.get(c, (d) => {
console.log(d)
})
})
})
Эта проблема уже давно преследует большинство JS-пользователей, и сообщество предлагает множество решений. Одна из таких схем, ставшая стандартной, называетсяPromise, вы можете разместить асинхронный обратный вызовОбернутый обещаниями,Зависит отPromise.then
Цепочка методов работает асинхронно:
const getB = a =>
new Promise((resolve, reject) => $.get(a, resolve))
const getC = b =>
new Promise((resolve, reject) => $.get(b, resolve))
const getD = c =>
new Promise((resolve, reject) => $.get(c, resolve))
getB(a)
.then(getC)
.then(getD)
.then(console.log)
Хотя ES7 уже имеет более лаконичный синтаксис async/await, промисы имеют очень широкий спектр приложений. Например, новая стандартная выборка для сетевых запросов будет инкапсулировать возвращаемый контент в виде обещания, и самая популярная библиотека Ajax axios делает то же самое. Что касается почтенной базовой библиотеки jQuery, которая когда-то занимала 70% веб-страниц, то Promise поддерживался уже в версии 1.5. Это означает, что пока вы делаете сетевой запрос на внешнем интерфейсе, вы в основном имеете дело с промисами.И сам Promise является своего рода Монадой.
Тем не менее, введение PROMISE в основном связано с миграцией различных состояний и использованием API, что звучит так, как будто Монада, кажется, совершенно не в состоянии это сделать. Какова связь между этими двумя концепциями? Чтобы прояснить этот вопрос, мы должны понять хотя бычто такое монада.
Зима Три Спрайт и Монада
Многих студентов, которые изначально были заинтересованы в изучении функциональных языков, таких как Haskell, может шокировать известная поговорка — [Разве монада — это не просто моноид на самофункторах, что трудно понять]. Фактически, это предложение похоже на то, что сказал Бай Ученый.Донгма Сяосан, Сюэкай Бичи] Никакой разницы, но это правильный бред, слушать людей знающих или понимающих, людей не понимающих или не понимающих. Так что если дальше и познакомить вас с Монадой так, то будьте уверены, что убили его - кормить и прочее, кто сказал, что зимние три Спрайта в самый раз!
специальный объект.
Поскольку это объект, его черная магия также существует в двух местах: свойствах и методах. Ниже мы должны ответить на важный вопрос:У монад есть какие-то специальные свойства и методы, которые помогут нам избежать ада обратного вызова?
мы можем использоватьочень простойпсевдокод для прояснения вопроса. Если у нас есть четыре вещи A B C D, то на основе вложенности обратных вызовов можно написать простейшее функциональное выражение вида:
A(B(C(D)))
Видите кошмар вложенных обратных вызовов? Однако мы можем упростить эту сцену по нитке. Во-первых, давайте сведем проблему к наиболее распространенному вложению обратного вызова:
A(B)
на основедобавить средний слойа такжеИнверсия контроляКонцепция , нам нужно всего дюжина строк кода для реализации простого промежуточного объекта P, передачи A и B этому объекту по отдельности, чтобы разделить обратный вызов:
P(A).then(B)
Теперь А заворачивается нами, П этоконтейнерЭто прототип Promise! В моем блогеКонцепция обещания и реализация из исходного кодаВ статье объяснялся основной механизм такого разложения обратных вызовов, и соответствующая реализация кода здесь повторяться не будет.
Однако это решение работает только при вложении между двумя функциями A b. Пока вы пытаетесь реализовать эту версию P, вы обнаружите, что у нас нет этой способности:
P(A).then(B).then(C).then(D)
Также не эта способность:
P(P(P(A))).then(B)
Здесь в игру вступают Монады! Сначала даем ответ:Этот объект является простой версией Monad.P
then
Конечно, этот API не является именем в ортодоксальной монаде, но для справки мы можем сначала взглянуть на одну из спецификаций Promise/A+.ключевые детали:
Каждый раз, когда мы разрешаем промис, нам нужно оценивать две ситуации:
- Если решаемый контент по-прежнему является обещанием (т.н.
thenable
),ТакРекурсивное решениеЭто Обещание. - Если содержимое, которое необходимо разрешить, не является промисом, то в соответствии с конкретной ситуацией содержимого (например, объекты, функции, примитивные типы и т. д.) перейдите к
fulfill
илиreject
Текущее обещание.
Интуитивно эта деталь гарантирует, что следующие два вызова полностью эквивалентны:
// 1
Promise.resolve(1).then(console.log)
// 1
Promise.resolve(
Promise.resolve(
Promise.resolve(
Promise.resolve(1)
)
)
).then(console.log)
Гнездо здесь выглядит знакомым? На самом деле это основная компетенция Monads in Promises: для P одеваться таккакой-то контентконтейнер, мы можемРекурсивно разберите контейнер слой за слоем и напрямую извлеките значение, содержащееся в самом внутреннем.只要实现了这个能力,通过一些技巧,我们就能够实现下面这个优雅的цепной вызовAPI:
Promise(A).then(B).then(C).then(D)
Это дает дополнительные преимущества: независимо от того, возвращает ли здесь функция BCD синхронно выполняемое значение или асинхронно разрешенный промис, мы можемточно так жеиметь дело с. Например, это синхронное дополнение:
const add = x => x + 1
Promise
.resolve(0)
.then(add)
.then(add)
.then(console.log)
// 2
и это слегка перевёрнутое асинхронное дополнение:
const add = x =>
new Promise((resolve, reject) => setTimeout(() => resolve(x + 1), 1000))
Promise
.resolve(0)
.then(add)
.then(add)
.then(console.log)
// 2
Независимо от того, синхронные и асинхронные, то, как они называются, точно такое же, как и конечный результат!
- простейший
P(A).then(B)
реализация, егоP(A)
Эквивалент монадыunit
Интерфейс, который можетЛюбое значение для контейнера упаковки Monad. - Для поддержки вложенных реализаций Promise его
then
За ним на самом деле в FPjoin
концепция,Когда в контейнере все еще есть контейнер, внутренний контейнер рекурсивно отсоединяется, и возвращается значение, содержащееся в нижнем слое. - Обещайте перезвонить цепь, на самом деле, это монад
bind
концепция. Вы можете объединить кучу квартир.then()
, передать различные функции, Promise может помочь вам сгладить разницу между синхронным и асинхронным, поместите эти функцииПрименить к значениям в контейнере по одному.
Возвращаясь к исходному вопросу в этом разделе,что такое монада? Пока объект имеет следующие два метода, мы можем считать его монадой:
-
Возможность обернуть значение как контейнер- В ФП это называется
unit
. -
Возможность применить функцию к значению, содержащемуся в контейнере- Сложность здесь в том, что контейнеры могут быть вложены в контейнеры, поэтому применение функции к значению требует рекурсии. В ФП это называется
bind
(Это то же самое, что и в JSbind
Совершенно два понятия, прошу не путать).
Как мы видели,Promise.resolve()
bind
. Следовательно, мы можем считать: Promise — это монада. На самом деле это не новый вывод, на Github уже давно есть доказательства с точки зрения кода, заинтересованные студенты могут пойтиПочувствуй это :-)
- расколоть
A(B)
дляP(A).then(B)
форма.这其实就是 Monad 用来构建容器的unit
. - Может писать независимо от синхронного или асинхронного
P(A).then(B).then(C)...
, который на самом деле находится в монадеbind
.
Здесь мы сможем понять роль Monad из функциональности Promise и использовать концепцию Monad для объяснения Promise его дизайна 😉
Что такое моноид на самом функторе
К этому моменту, если вы понимаете Promise, вы уже должны понимать Monad. Однако как быть с моноидом на самом функторе в легенде о монадах? На самом деле, пока вы читаете это, вы уже виделиавтофунктора такжемоноид(Понимание здесь может быть неточным, право следует использовать для привлечения большего количества идей, надеюсь, Далао это поправит).
автофунктор
ФункторТак называемый Functor — это функция, которая может помещать в него значения и преобразовывать содержимое контейнера, передавая функции.контейнерPromise.resolve
就相当于这样的映射,能把任意值装进 Promise 容器里。 а такжеавтофункторPromise(A).then()
моноид
Моноид, так называемый монадический, удовлетворяет двум условиям: тождественности и ассоциативности.
Единица: юаньэти два условия:
Сначала подействуем на единичный элементunit(a)
Вверхf
, результат иf(a)
Последовательный:
const value = 6
const f = x => Promise.resolve(x + 6)
// 下面两个值相等
const left = Promise.resolve(value).then(f)
const right = f(value)
Во-вторых, воздействуя на неединичные элементыm
Вверхunit
, результат все равноm
сам:
const value = 6
// 下面两个值相等
const left = Promise.resolve(value)
const right = Promise.resolve(value).then(x=> Promise.resolve(x))
Что касаетсяассоциативностьЭто условие:(a • b) • c
равныйa • (b • c)
:
const f = a => Promise.resolve(a * a)
const g = a => Promise.resolve(a - 6)
const m = Promise.resolve(7)
// 下面两个值相等
const left = m.then(f).then(g)
const right = m.then(x => f(x).then(g))
Приведенные выше короткие строки кода на самом деле являются доказательством [Promise is a Monad]. На данный момент мы можем обнаружить, что при написании обещаний с интерфейсом ежедневной стыковки все, что мы пишем, может быть обновлено до уровня монад функционального программирования, а затем объяснено с помощью абстрактной алгебры и теории категорий.Моментально ли сила увеличивается?XD
Суммировать
Все вышеперечисленные аргументы не касаются>>==
Это содержимое Haskell, мы можем в полной мере использовать такой низкий порог языка JS Monad для представления того, что такое использование. В некоторой степени я согласен с мнением Ван Инь: порог функционального программирования искусственно превышает или миф, и, очевидно, фактическое развитие очень практично и легко понять, нужно использовать более сложную концепцию, чтобы понять формализацию определить и объяснить, я боюсь Это не способствует популярности отличных инструментов и идей.
BumpoverПри обещании нового понимания. Заинтересованные студенты приветствуют внимание OH XD