предыдущая статьяКто первым выполнит setTimeout и setImmediate?Эта статья даст вам полное представление о цикле событий.Подробно объясняется асинхронный API браузера и Node.js, а также лежащий в его основе принцип Event Loop. В этой статье будет рассказано о том, как добиться асинхронных эффектов без использования нативного API, то есть режима публикации-подписки. Модель публикации-подписки также является высокочастотным тестовым сайтом в интервью.В этой статье будет реализована модель публикации-подписки сама по себе.Поняв ее принцип, мы можем читать Node.jsEventEmitter
Исходный код, который также является типичной моделью публикации-подписки.
Все примеры в этой статье были загружены на GitHub, и все мои сообщения в блоге и примеры находятся в том же репозитории:
Зачем использовать модель публикации-подписки?
В отсутствиеPromise
Раньше мы часто использовали обратные вызовы при работе с асинхронными API, но если есть несколько асинхронных вызовов API, которые зависят друг от друга, слишком много уровней обратных вызовов может привести к «аду обратных вызовов». Следующий код демонстрирует, что если у нас есть три сетевых запроса, второй должен дождаться завершения первого, прежде чем его можно будет выдать, а третий должен дождаться завершения второго, прежде чем его можно будет выдать.Если мы используем обратные вызовы, станет вот так:
const request = require("request");
request('https://www.baidu.com', function (error, response) {
if (!error && response.statusCode == 200) {
console.log('get times 1');
request('https://www.baidu.com', function(error, response) {
if (!error && response.statusCode == 200) {
console.log('get times 2');
request('https://www.baidu.com', function(error, response) {
if (!error && response.statusCode == 200) {
console.log('get times 3');
}
})
}
})
}
});
Поскольку ajax на стороне браузера будет иметь междоменные проблемы, я запустил приведенный выше пример с Node.js. В этом примере три слоя обратных вызовов, и мы уже немного закружились, если слоев будет больше, это будет действительно «ад».
модель публикации-подписки
Шаблон публикации-подписки — это шаблон проектирования, который используется не только в JS, этот шаблон может помочь нам решить «ад обратных вызовов». Его процесс показан ниже:
- Центр сообщений: отвечает за хранение переписки между сообщениями и подписчиками и уведомление подписчиков, когда сообщение инициируется.
- Подписчики. Подпишитесь на интересующие вас сообщения Центра сообщений.
- Издатель: при выполнении условий публиковать сообщения через центр сообщений
В этом режиме нет необходимости попадать в «ад обратных вызовов» при работе с несколькими взаимозависимыми асинхронными API на фронте, нужно только подписаться на предыдущее сообщение об успехе и опубликовать сообщение после предыдущего успеха.
Реализуйте модель публикации-подписки самостоятельно
Зная принцип, давайте самостоятельно реализуем модель публикации-подписки, на этот раз для ее реализации воспользуемся классом ES6.Если вы не знакомы с объектно-ориентированным классом JS или классом ES6, прочитайте эту статью.:
class PubSub {
constructor() {
// 一个对象存放所有的消息订阅
// 每个消息对应一个数组,数组结构如下
// {
// "event1": [cb1, cb2]
// }
this.events = {}
}
subscribe(event, callback) {
if(this.events[event]) {
// 如果有人订阅过了,这个键已经存在,就往里面加就好了
this.events[event].push(callback);
} else {
// 没人订阅过,就建一个数组,回调放进去
this.events[event] = [callback]
}
}
publish(event, ...args) {
// 取出所有订阅者的回调执行
const subscribedEvents = this.events[event];
if(subscribedEvents && subscribedEvents.length) {
subscribedEvents.forEach(callback => {
callback.call(this, ...args);
});
}
}
unsubscribe(event, callback) {
// 删除某个订阅,保留其他订阅
const subscribedEvents = this.events[event];
if(subscribedEvents && subscribedEvents.length) {
this.events[event] = this.events[event].filter(cb => cb !== callback)
}
}
}
Решить ад обратного вызова
есть собственныйPubSub
, мы можем использовать его для решения предыдущей проблемы с адом обратных вызовов:
const request = require("request");
const pubSub = new PubSub();
request('https://www.baidu.com', function (error, response) {
if (!error && response.statusCode == 200) {
console.log('get times 1');
// 发布请求1成功消息
pubSub.publish('request1Success');
}
});
// 订阅请求1成功的消息,然后发起请求2
pubSub.subscribe('request1Success', () => {
request('https://www.baidu.com', function (error, response) {
if (!error && response.statusCode == 200) {
console.log('get times 2');
// 发布请求2成功消息
pubSub.publish('request2Success');
}
});
})
// 订阅请求2成功的消息,然后发起请求3
pubSub.subscribe('request2Success', () => {
request('https://www.baidu.com', function (error, response) {
if (!error && response.statusCode == 200) {
console.log('get times 3');
// 发布请求3成功消息
pubSub.publish('request3Success');
}
});
})
EventEmitter для Node.js
Node.jsEventEmitter
Идея та же, что и в нашем предыдущем примере, но в ней больше обработки ошибок и больше API.Исходный код доступен на GitHub:GitHub.com/node будет /node…. Давайте рассмотрим несколько API:
Конструктор
Кодовый портал:GitHub.com/node будет /node…
Конструктор очень простой, всего одна строчка кода, основная логика вEventEmitter.init
в:
EventEmitter.init
Он также выполняет некоторую работу по инициализации.this._events
написано с намиthis.events
Функция та же самая, используется для хранения подписанных событий. Код ядра отмечен на схеме стрелками. Здесь следует отметить одну вещь: если есть только одна подписка на тип события,this._events
Это прямая функция, а не массив, в котором мы будем неоднократно видеть источник этого суждения, чтобы улучшить производительность записи.
Подписаться на события
Кодовый портал:GitHub.com/node будет /node…
EventEmitter
API для подписки на события:on
а такжеaddListener
, из исходного кода мы видим, что эти два метода совершенно одинаковы:
Оба метода называются_addListener
, этот метод оценивает и обрабатывает ошибки в параметрах, а основной код по-прежнемуthis._events
Добавить событие внутри:
Опубликовать событие
Кодовый портал:GitHub.com/node будет /node…
EventEmitter
API для публикации событийemit
, этот API будет выполнять специальную обработку событий типа «ошибка», то есть выдавать ошибки:
Если это не событие неправильного типа, удалите подписанное событие обратного вызова и выполните его:
отписаться
Кодовый портал:GitHub.com/node будет /node…
EventEmitter
API для отписки внутри:removeListener
а такжеoff
, они абсолютно одинаковы.EventEmitter
API отмены подписки не только удалит соответствующую подписку, но и выдастremoveListener
события для уведомления внешнего мира. прямо здесь тожеthis._events
внутри соответствующегоtype
Судить, если есть только одно, то есть этоtype
Типfunction
, ключ будет удален напрямую. Если подписок несколько, подписка будет найдена, а затем удалена. Если все подписки удалены, просто удалитеthis._events
Заглушка:
Суммировать
В этой статье объясняется принцип режима публикации-подписки и реализуется простой режим публикации-подписки. Поняв принцип, я также прочитал Node.jsEventEmitter
Исходный код модуля дополнительно учит, как написать режим публикации-подписки в производственной среде. Подводя итог, можно сказать, что модель публикации-подписки имеет следующие характеристики:
- Решенный «ад обратного вызова»
- Разделение нескольких модулей, когда вы выполняете его самостоятельно, вам не нужно знать о существовании другого модуля, вам нужно только заботиться об опубликованных событиях.
- Поскольку несколько модулей могут не знать о существовании друг друга, события, о которых они заботятся, могут быть выпущены в очень дальнем углу, и вы не можете напрямую найти место, где события выпускаются с помощью переходов кода, что может быть немного сложно отлаживать. .
В конце статьи спасибо, что потратили свое драгоценное время на чтение этой статьи. Если эта статья немного поможет вам или вдохновит, пожалуйста, не скупитесь на лайки и звезды GitHub. Ваша поддержка является движущей силой для автор продолжать творить.
Добро пожаловать, чтобы обратить внимание на мой общедоступный номербольшой фронт атакиПолучите высококачественные оригиналы впервые~
Цикл статей "Передовые передовые знания":nuggets.capable/post/684490…
Адрес GitHub с исходным кодом из серии статей «Advanced Front-end Knowledge»:GitHub.com/Денис — см....