This course has been updated! We now recommend you take the The Good Parts of JavaScript and the Web course.
Transcript from the "The Promise Monad (A)" Lesson
[00:00:00]
>> [MUSIC]
[00:00:04]
>> Douglas Crockford: So we want to find a better way of structuring that kind of stuff, and that thing is promises. Promises come out features which came out of the actual model, this is another of the benefit of the actual model. Promises are an excellent mechanism for managing asynchronicity. A promise is an object that represents a possible future value.
[00:00:29] So we can have an operation which will return something, which indicates something that might happen later but hasn't happened yet, but it contains that potential. Every promise has a corresponding resolver that is used to ultimately assign a value to that promise, and every promise has one of three states, kept, broken, or pending.
[00:00:49] It's created pending and can move to one of the other two states and after that it will never change again
>> Douglas Crockford: A promise is also an event generator, it fires its event when the value of the promise is ultimately known. And at any time after the making of a promise, event handling functions can be registered with the promise, which will be called in the order with the promise's value when it is known.
[00:01:14] And a promise can accept functions that will be called with the value once the promise has been kept or broken. So promise.when can take two functions, success or failure. And which one will be called will depend on the ultimate resolution of the promise.
>> Douglas Crockford: So here's some code, I have a macroid called vow, which I used to make vows.
[00:01:41] A vow contains a keep function, a break function, and a promise object. The keep function I call when I know what the value of the promise is, and I want to keep the promise. If I know that the promise will never be kept, I can break it, and explain why.
[00:01:59] Then the promise itself takes a when method, and that when method can take two functions, one of which may be called, depending on the result of the function. And when itself returns another promise, so based on result of that we can do more stuff, so we get a level of compossibility with these things.
[00:02:23] So here's an example of how you might use it. We have done file IO wrong ever since four trap, it's always been a break in weight kind of thing. So when you call the file system for a file, that could mean several process which is, it can be waiting for rotation, it can be waiting for your place in the IO queue, it can be waiting for a lot of stuff.
[00:02:46] We don't wanna wait in return based system. So the other way to do it is to have the file system immediately return a promise. And that promise will be made good once the file system gets around to reading the file. So this is how I think file system should work and no JS comes really close to this.
[00:03:07] So I read_file, give it a file name. And I'll say when we have the result of that file, call success function and it will get the string, which represents the contents of the file. So that's how we do it, we treat it just like treat events in the browser.
[00:03:23] Waiting for the user to click on something, waiting for a file to get read, works exactly the same way. And we also provide a failure function to be called if for some reasons they can't open the file, like the file can't be found or I don't have access or whatever.
[00:03:38] You might wonder why do we call a failure function, why don't we just throw an exception? It's because of the nature of turns that, so an exception modifies control flow by unwinding the stack, right? Say we got an error here, let's step back until we find somebody who wants to catch it, and then we can go forward.
[00:04:02] But in a turn-based system, the stack is emptied at the end of every turn. Cuz every turn program runs to completion when it is finished, there's no more stack. So it's like time travel, there's no way you can throw something into a previous turn, cuz that time has ended.
[00:04:20] We can only go forward, and the mechanism we have for going forward is promises. Okay, so the failure branch of the promise is the way we send exceptions into the future.
>> Douglas Crockford: And we can cascade failures as well. So here I've got a promise, and I'll call its when method passing a, and it returns a promise.
[00:04:45] I'll call it's thing and say call it success_b, it returns a promise. We'll call it's thing and a failure. So success_a gets the result of the promise and b gets the result of a, and c gets the result of b. But if any of those fail then the failure propagates to this failure method.
[00:05:08] So this chaining of failure means that this acts sort of like the try. I can have all of these things which are being resolved in different terms. And if any of them go wrong for any reason, that is a function that I know gets called. So it gives me a much better way of controlling this stuff, keeping track of what's going on.