34

In every tech talk, or in every blog post I've read about IEnumerable and IObservable I read that, IEnumerable is pull-based structure and IObservable is push-based structure.

I've read that with IObservable we have async calls, where nothing is blocked and everything is push based.

But,but,but...

What does it really means? Push based and pull based

Because in my opinion in IEnumerable we can also push data into the structure and also pull data from it, I've really lost in that technical terms and ideas.

Please in a normal and human way explain to me the difference between that two structure and difference between push-based and pull-based structures.

Thanks.

Tornike Gomareli
  • 1,564
  • 2
  • 16
  • 28

4 Answers4

55

Please in a normal and human way explain to me the difference

OK, let's make some toast. That's the most normal human thing I do.

Pull based:

// 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.

Push based:

// 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.

You want to pull a result, you call a function and you do nothing else until the function returns.

You want a result pushed to you, you call an async function and await the result. When the result is available it is pushed at the callback, which resumes the method where it needs to be.

IEnumerable<T> is just a sequence of pulls; you call MoveNext every time you want to pull a result and you get a T. IObservable<T> is just a sequence of pushes; you register a callback, and it is called every time a new T is available.

Put another way: IEnumerable<T> is logically a sequence of Func<T> invocations. IObservable<T> is logically a sequence of Task<T> continuations. Don't let the fact that they are sequences confuse you; that's incidental. The fundamental idea is that functions are synchronous; you call them and get a result synchronously; if it takes a while, you wait. Tasks are asynchronous; you start them and get the result asynchronously when it is available.


This idea existed in C# before IObservable and await. Another way to look at is is: pull-based is like function calls, push-based is like event handlers. A normal function call, you call it when you want something. An event handler, it calls you when something happens. Events are how the C# language itself represents the observer pattern. But events always logically form a sequence, so it makes sense to be able to manipulate a sequence of pushed items the same way we'd manipulate a sequence of pulled items. And hence, IObservable was invented.

Eric Lippert
  • 647,829
  • 179
  • 1,238
  • 2,067
  • 12
    Now I want toast. – Sach Jul 09 '18 at 21:28
  • 5
    One of the greatest answers I've ever seen on this topic. Thank you very much, I will now make toast in async way – Tornike Gomareli Jul 09 '18 at 21:28
  • 3
    I usually do my toasts with a hot-air gun, and cannot afford to do something else while doing so. Otherwise the toast would be toast. I don't want to push the limits here. Excellent answer! –  Jul 09 '18 at 21:33
  • @elgonzo You can avoid ditching your hot-air gun for an observable toaster by buying a hot-air gun that implements the IAsyncEnumerable interface. – Sentinel Jul 10 '18 at 08:14
  • 1
    Eric I am a bit skeptical with comparing an `IObservable` with a sequence of `Task` continuations. With an observable you subscribe once to get multiple notifications. With a sequence of tasks you must subscribe to each task individually (by awaiting it), to get a single notification from each subscription. I would also like to know your opinion about the `IAsyncEnumerable`s. I am confused whether these should be considered pull or push structures. It seems that there are arguments for both points of view. – Theodor Zoulias Dec 04 '20 at 09:07
12

A man walks into a grocery store and asks the shopkeeper if he has any eggs. "Yes," says the shopkeeper. "May I have some?" asks the man. And the shopkeeper gives the man some eggs. "Do you have any more?" asks the man. "Yes," says the shopkeeper. "May I have some?" asks the man. And the shopkeeper gives the man some eggs. "Do you have any more?" asks the man. "No," says the shopkeeper. The man leaves.

That's pull-based. The man keep "pulling" eggs from the shopkeeper until there were none left.

A man walks into a grocery store and asks the shopkeeper if he can delivery any eggs and, if so, can he deliver then whenever he can get them. "Yes," says the shopkeeper. The man leaves. In a few days some eggs arrive. A few more days later some eggs arrive. Then the man calls the shopkeeper and asks the for the deliveries to stop. No more eggs arrive.

That's push-based. The man doesn't wait for the shopkeeper to give him eggs. The man goes about doing something else and the shopkeeper "pushes" the eggs to the man.

Enigmativity
  • 113,464
  • 11
  • 89
  • 172
10

Assuming a (logical) server and a client, who determines when data is delivered, the server or the client?

Pull-based describes a client-run scheme: Clients make requests, and the data is served up immediately. Push-based describes a server-run scheme: Clients can connect to a push-stream (IObservable), but they can't demand data, they get it when the server feels like giving it.

A canonical form of pull would be a database query: You send a request to the server, and the server responds with a collection of items. A canonical version of push would be a chat app: The chat client can't 'demand' new conversation data, the server will tell you when the other person has said something.

Shlomo
  • 14,102
  • 3
  • 28
  • 43
2

In addition to the above great answers, I would also offer the following:

  • 'IEnumerable' meaning 'enumerable' is implicitly confused with 'pull based' in the .NET framework conceptual model. It is intended to mean "allows you to get an enumerator, which allows you pull the next value"
  • But we also have IAsyncEnumerable.
  • Mathematically, enumerable just means 'countable,' i.e. a countable set.
  • Observables may be countable too, in the sense that an observable may represent a set of discrete events.

The naming is confused and in my opinion poorly represent the concepts they intend. For example, in the Java world IEnumerable is Iterable, which is closer to the intention in .NET.

I think it is simplest to imagine IEnumerable and the LINQ constructs around them as "Get me some data from some source and filter/group them like this..." whereas Observables can be thought of as incoming streams that you can react to. I don't think it is necessarily helpful to think of observables as continuations.

Sentinel
  • 3,582
  • 1
  • 30
  • 44