В чем разница между структурами на основе push и pull, такими как IEnumerable <t> и IObservable <t>

В каждом разговоре по технологиям или в каждом сообщении в блоге, которое я читал о IEnumerable и IObservable, я читал, что IEnumerable - это структура, основанная на pull-based, и IObservable - это основанная на push структура.

Я читал, что с IObservable у нас есть асинхронные вызовы, где ничего не заблокировано, и все работает на основе push.

Но но НО...

Что это значит? На основе push и pull

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

Пожалуйста, нормальным и человеческим способом объясните мне разницу между этими двумя структурами и разницей между структурами на основе push и pull.

Благодарю.

Ответ 1

Пожалуйста, нормальным и человеческим способом объясните мне разницу

Хорошо, сделай тост. Это самая нормальная человеческая вещь, которую я делаю.

Основание:

// I want some toast.  I will pull it out of the toaster when it is done.
// I will do nothing until the toast is available.
Toast t = toaster.MakeToast();
t.AddJam();
// Yum.

Нажатие:

// I want some toast.  But it might take a while and I can do more work
// while I am waiting:
Task<Toast> task = toaster.MakeToastAsync();
Toast t = await task;
// await returns to the caller if the toast is not ready, and assigns
// a callback. When the toast is ready, the callback causes this method
// to start again from this point:
t.AddJam();
// Yum.

Вы хотите получить результат, вы вызываете функцию, и вы ничего не делаете до тех пор, пока функция не вернется.

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

IEnumerable<T> - это просто последовательность тяг; вы вызываете MoveNext каждый раз, когда хотите вывести результат, и получите T IObservable<T> - это просто последовательность IObservable<T>; вы регистрируете обратный вызов, и он вызывается каждый раз, когда доступен новый T

Иными словами: IEnumerable<T> логически является последовательностью вызовов Func<T>. IObservable<T> логически является последовательностью продолжений Task<T>. Не позволяйте тому факту, что они представляют собой последовательности, путают вас; это случайный. Основная идея состоит в том, что функции синхронны; вы называете их и получаете результат синхронно; если это займет некоторое время, вы ждете. Задачи асинхронны; вы запускаете их и получаете результат асинхронно, когда он доступен.


Эта идея существовала в С#, прежде чем IObservable и await. Другой способ взглянуть на это: pull-based похож на вызовы функций, push-based подобен обработчикам событий. Обычный вызов функции, вы называете это, когда хотите что-то. Обработчик событий, он вызывает вас, когда что-то происходит. События - это то, как сам язык С# представляет шаблон наблюдателя. Но события всегда логически формируют последовательность, поэтому имеет смысл манипулировать последовательностью нажатых элементов так же, как мы будем манипулировать последовательностью вытащенных элементов. И, следовательно, был изобретен IObservable.

Ответ 2

Предполагая (логический) сервер и клиент, который определяет, когда данные доставляются, сервер или клиент?

Pull-based описывает схему клиентского запуска: клиенты делают запросы, и данные немедленно подаются. Push-based описывает схему запуска сервера: клиенты могут подключаться к потоку-потоку (IObservable), но они не могут требовать данных, они получают его, когда сервер чувствует, что дает ему.

Канонической формой pull будет запрос базы данных: вы отправляете запрос на сервер, а сервер отвечает набором элементов. Каноническая версия push будет чат-приложением: клиент чата не может "требовать" новых данных о разговоре, сервер скажет вам, когда другой человек что-то сказал.

Ответ 3

В дополнение к вышеупомянутым замечательным ответам я бы также предложил следующее:

  • "IEnumerable", означающий "enumerable", неявно смешивается с "pull based" в концептуальной модели платформы.NET. Он предназначен для обозначения "позволяет вам получить счетчик, который позволяет вытащить следующее значение"
  • Но у нас также есть IAsyncEnumerable.
  • Математически перечислимое просто означает "счетный", т.е. Счетный набор.
  • Наблюдаемые также могут быть счетными, в том смысле, что наблюдаемый может представлять собой набор дискретных событий.

Именование путается и, на мой взгляд, плохо отражает концепции, которые они намереваются. Например, в мире Java IEnumerable является Iterable, что ближе к намерению в.NET.

Я думаю, что проще всего представить IEnumerable и LINQ-конструкции вокруг них: "Получите мне некоторые данные из какого-то источника и фильтруйте их/группируйте их так...", тогда как Observables можно рассматривать как входящие потоки, на которые вы можете реагировать. Я не думаю, что всегда полезно рассматривать наблюдаемые как продолжения.

Ответ 4

Человек входит в продуктовый магазин и спрашивает лавочника, есть ли у него яйца. "Да", - говорит лавочник. "Могу ли я взять с собой?" - спрашивает мужчина. И лавочник дает человеку несколько яиц. "У тебя есть больше?" - спрашивает мужчина. "Да", - говорит лавочник. "Могу ли я взять с собой?" - спрашивает мужчина. И лавочник дает человеку несколько яиц. "У тебя есть больше?" - спрашивает мужчина. "Нет", - говорит лавочник. Человек уходит.

Это основано на тяге. Мужчина держит "вытаскивание" яиц у лавочника до тех пор, пока их не осталось.

Человек входит в продуктовый магазин и просит лавочника, если он может достать яйца, и если да, то может ли он доставить то, когда он сможет их получить. "Да", - говорит лавочник. Человек уходит. Через несколько дней приходят яйца. Еще через несколько дней приходят некоторые яйца. Затем человек называет лавочника и просит, чтобы поставки прекратились. Больше яиц не поступает.

Это толчок. Мужчина не ждет, пока лавочник даст ему яйца. Человек делает что-то еще, и лавочник "подталкивает" яйца к человеку.