So what's happened here is promises actually take care -- they catch any errors that occur, and forward them up until they're actually caught, where you pass an error handler, notice on get stock price, we pass an error handler. We don't have to catch errors at every possible step in the function. We just catch errors in one place and the promise type will catch errors and forward it up until -- there's a function provided to handle that error. So what's actually happening is that type is doing the job that try/catch does for you in the language. So that's how we got back down to that nice little piece of code. That's an improvement. No more pyramid of doom. It's not bad. But why can't we just write code that looks pretty much the same, regardless of whether it's waiting or blocking, right? What if we could just write our algorithm and make it easy to switch that algorithm between waiting and blocking? That promise code you saw on the previous slide looks different from the synchronous blocking code you saw earlier. As of ES2015, you will now be able to wait like this.
So for each one of these yields, we have that return which returns the tuple, the value, and the done, and increments the state variables, as it goes along, and that's how we move through the states. This allows us to use iteration. The whole process of pulling a value out of it until you're done is called iteration. And it involves a consumer and a producer. The consumer requests an iterator from the producer, and then it pulls the value out by calling next. So the producer emits a value. Right? And why do I say it's pull? Well, notice I'm getting the value out in the return position of next. So I'm pulling functions out. I'm pulling values out. I keep pulling values out, until finally I pull out a value and the producer says you're done. So that's how iteration works. You keep pulling values out until the producer says I'm done. No more data.
So if generator functions return iterators, why are they called generator functions? Why don't we just call them iterator functions? There's more than meets the eye when it comes to generator functions. A generator is actually built out of two different interfaces. The iterator you saw earlier, where you can call next and get out the little tuple saying what the value is and whether you're done -- it also has an entirely other side, a split personality, which is an observer interface, which gets push values. So a generator is both a source of data, which you can pull data out of, but it's also a sync which you can push data into. So these two sources combined create a generator. A generator is an iterator. You can pull a value out. If you attempt to pull a value out, you can get an error, because it might throw if you call next. And finally you can pull a value out and get that done:true in that little tuple. So there's three types of notifications that a producer can tell you during iteration. I've got some data. An error. And here's your final value. You can see all of those notifications in reverse on the observer side. So when you call next on a generator, you can also push a value in by passing a value in the argument position of next.
You can also send an error in, by calling throw, and finally, you can send in a final value by calling return. So why does an observer have this sort of split personality of both being a source of data and sync? It's to solve a little problem. And that's that iteration only allows data to flow in one direction. Whereas with generators, two functions can effectively have a conversation. Have a long running conversation. I put a value out. I push a value back in. I pull a value out, I push a value back in. Why would I want to do that? What does that buy me? It allows us to use a very powerful pattern that I think a lot of people in the room are going to be using in the next few years called asynchronous iteration. So we have our generator function and the first thing I want you to notice is that I told you to use a mental model for understanding yield. The model I gave you was -- it's sort of like an intermediary return. But in this particular piece of code, it's used like an expression. We're siding to the result of a yield. So what's that about? Here we have a producer function.
And what it's actually going to produce if you notice what's on the right hand side of those yields -- it's going to be an iterator of promises. So we're going to pull promises out of this iterator, and then what we're going to do is we're going to resolve those promises and then push the value back in. And so we need a consumer to go along with this producer. The consumer's job is just going to be to pull out, by calling next, pull out promises, resolve those promises, and push the value back in. And what you're going to see later is when you push a value back into the generator, the whole yield expression gets replaced with the value that you put back in. And this is the key. This is the recipe to allow you guys to write code that looks -- that looks like it blocks, but actually waits. So let's take a look at how this works. We have this spawn function, which I'm going to define in just a moment. Think about as the consumer -- it's pulling promises out of this iterator promise. So if I run this, what spawn is going to do is it's going to produce a promise which will be the eventual result of get stock price, after we resolve the get stock symbol promise and the get stock symbol price promise. So when I run this code eventually it's going to run the price of an individual stock. In this case, Pfizer stock. Well, asynchronous iteration works this way. You've got a consumer and a producer. The consumer requests a generator from the producer. And then calls next, pulls out the first value, but notice -- execution suspends at the first yield. And what we're yielding back is a promise. It's a promise that will eventually resolve to the symbol for Pfizer. And so that promise is handed to the consumer, and the consumer and the producer have an agreement. The consumer says -- every promise I pull out, I'm going to resolve, and then I'm going to push the value back into the producer.
So the consumer calls then on the promise, waits until it's resolved, and then calls next again, on the generator, from the producer, except this time -- notice we pass PFE in the argument position of next. We're effectively pushing a value in, at the same time as we're pulling a value out. So we send PFE back in, and now I want you to look at what happens to the yield expression. It effectively gets replaced with a value that we get pushed in, and then execution resumes, and pauses at the next yield. So now the consumer's pushed in a value, and at the same time, they're pulling out another promise. This time for the price of that particular stock. So agreement between the consumer and the producer -- the consumer is going to resolve that promise. And when it gets a value, it's going to call next again, and this time, push 2783 back into the producer. That replaces yield. And we continue and stop at the final return value. So now we've actually returned -- it's not a promise. It's just a plain old value. Back to the consumer. And we said -- hey, we're done. We're not going to give you any more data. At this point, the consumer takes the promise that it returned, the spawn function, and it resolves it to the final value, 2783. So pictures are great. But let's take a look at some code.
So here I have get stock price. And then I have this spawn function, which consumes it. So notice at the bottom I'm calling spawn, but I'm passing in the generator that comes out of get stock price into spawn. And what's going to come out of that is a promise that's eventually going to resolve to the final price of that stock. So I run this. And what happens is we immediately create a promise inside of the spawn function. And execution runs to the very bottom and hits this onresult function. And this function is basically my way of asynchronously looping and continuing to resolve promises. You're going to see me recursively call this function again and again, every single time I get a new value out of a promise. So we go up here, first time around, the last result is undefined, haven't gotten any yet, as soon as I call next, notice up there at the top, execution stops at the first yield and we return a promise that symbolizes the eventual stock symbol. And that gets returned to the consumer. If it's a promise, the consumer calls then on it. Notice here we're passing on the onresult function we started with. If but this time when the on result function is called it's going to get the result of that promise. So as soon as I do this, we're going to hop up back here, and the last promise result is going to be whatever came out of that promise. JNJ. So now I got the result of the promise as a producer, but I want to push it back -- excuse me, the consumer. I want to push it book back to the producer. So I call next. I pass back in JNJ as the argument, you look at the top there, notice what happens to the yield symbol promise expression -- it gets replaced with JNJ and execution resumes and stops at the next yield point. So get symbol price is a promise that eventually resolves to the price. That's what comes out to the consumer. Another promise. We call then on it again. Pass in on result, that recursively gets resolved to the next promise, which is the price. We call next, yield gets replaced with that price, and then we continue on to the very end of the generator function, at which point it comes out and says -- you know what? Done is true. We're not going to get any more values. Consumer takes that, doesn't need to resolve it, it's not a promise, and then resolves the overall promise that was returned from spawn to that final value.
No callbacks required. I'm just looping over a stream of information being pushed at me. So this would be great to do. Right? Now when I run this, it resolves to a promise to give you the next price spike in this stream of stock prices. But there's a little problem. There's a reason we can't have nice things just yet. Right? The problem is that the web has no standard observable interface. We have iterable. As of ES6, this contract you saw earlier, what you call symbol.iterator. But today we have this proliferation of different APIs that push us streams of data. That push us data in a callback in a streamed way. DOM events, web sockets, node streams, XHTML requests -- can all push you values. But they don't implement one common interface. So one thing proposed for ES2016 is the observable contract. So here we have the iterable contract, introduced in 2015. How are we going to get the observable contract? It's hidden inside of this type. If we just swap the arguments and the return type of the iterable what pops out is an observable. An observable accepts a generator, and then uses the push side of the generator, and pushes multiple values in it until it finally calls return to signal that no more values are coming. If you think about it, iteration and observation are both about the same thing. Both about a producer giving a consumer multiple values. The difference is in one circumstance with DOM events, the data is being pushed to you.
But when you're using an iterator to pull values out of an array, you're pulling values. Right? In iteration, the consumer is in control, and in observation, the producer is in control. The web socket decides when it calls you. So it's sort of like they're it rating you. The producer is iterating you by calling your callback. So how does observation work? The consumer and producer and the relationship with iteration -- it's the exact same process but kind of the inverse. So here the producer, instead of the consumer requesting a generator from the producer, the producer -- or the consumer hands a generator to the producer. And in this context, just think about a generator as like three callbacks. It's got the next, the throw, and the return callbacks. Just like you're handing an API three callbacks and expecting it to push information to you by calling your callbacks. So the producer produces a value and calls the next method on your generator, the one you provided to it, and that's how it pushes 42 to you, and pushes another value and at its leisure, decides to push 39 to you, until finally it says you know what? There's no more data coming. So it calls return to indicate to you no more data will arrive.
So observation and iteration are actually deeply linked. As we saw, we can turn one inside out and get the other. So this is what it would look like to consume a web socket or a DOM event that implemented this contract. You could just walk up to any of these data sources and hand it a generator, which is these three callbacks, and then it will just push streams of data at you. Until it tells you it's done. So we can add sugar, just like we added sugar for iteration, for that whole process of calling symbol.iterator, and that while loop, and checking the done property, just like we can add sugar for that, we can add sugar for observation. A 4H method, for example, to an observer that returned a promise when it resolved, or, as I showed you guys earlier, now that we have a well defined method for observation, which we do, a for on loop. So the push stream can be done entirely without callbacks. So the hope would be if we introduce observable, all the push APIs on the web can implement this contract and all of a sudden we can just add language support magically for all of these different push data sources.
So if I wanted to consume an observable in ES2016, at least as the proposal stands, I could do something like this. And this is just going to keep printing out new sign-ups at Netflix. Just going to keep going. So... If an async function returns a promise, and a function star returns an iterator, we as language designers have a question to answer. What does an async function star return? You can't just go adding features to languages and not describing what happens when you put them together, when they interact. Well, there's a couple of different opinions about this. And there's actually a couple different ways the committee could go. I'm here today to tell you about one particular option. So if we look at this table, we've got synchronous functions, which return a value, synchronous generators, which return multiple values, you can pull multiple values out of -- but we also have asynchronous functions, which return a promise, and a promise pushes you one value. Right? You give it a callback and it pushes the value on that callback. But what goes here?
You guys can stop worrying about the machinery and the callbacks and just focus on your code. So... This particular proposal -- I just want to call out -- is at the strawman phase. The earliest phase of the process. So we're definitely going to be thinking about this more, and this may not make it through as a final feature. Just want to make that clear. It's very early in the feature stages. We're looking for feedback on this. So we want to hear from you about whether this works for you and solves your problems. So thanks very much, everybody. Here are some resources for you, and that's the end of my talk.