HTML-5 browsers provide a natively implemented JavaScript Promise. That means, you don't need a JS library any more to use Promises, they are now built into the language like String and Date.
Do we need promises? No, we don't. What Promises do has been done by JS programmers for more than 15 years now. It is just a single deferred callback with explicit error handling. Another toy, or a best practice that hopefully will not cause too many abuses and misunderstandings.
I searched the web for a short and concise tutorial about Promise (or Future, or Deferred) that just explains when and how to use it, but I didn't find any. So here is mine.
Try Out
Characterizing a Promise
You could use a Promise when following characterization applies to your use-case.
- A promise is something that will happen in future, and we don't know when, it is asynchronous, we can not retrieve an immediate result from a promise
- as nobody can know what future brings, the promise could fail
- a promise fulfills (or fails) when something asynchronous happens, it hangs until then, thus a Promise is "stateful"
- a promise fulfills (or fails) just once, not several times
For example, a Promise could perfectly perform an AJAX call. The processing of something that an HTML page fetches from its server asynchronously.
But a Promise is not a background-thread. Until now we don't have threads in JavaScript.
Promise Building Blocks
Following is needed to build a Promise:
- an executor function (promise body), the AJAX call, executed synchronously, initiating something asynchronous
- a resolve function (fulfill), what should be done when the call succeeds, executed asynchronously
- a reject function (fail), what should be done when the call fails, executed asynchronously
Just the resolve
and reject
functions can return a value.
This value would be passed to the next Promise, created by the promise's then()
function.
The promise's then()
function should be called with two parameters, both functions, both could be undefined
:
promise.then(resolveFunction, rejectFunction)
The then()
function returns another Promise instance,
so that you can chain promises together. (If you can't get enough of them:-)
Mind that the Promise itself is NOT asynchronous, it is the executor
function that initiates something asynchronous.
The executor
also must make sure that this asynchronous thing calls the resolveFunction
on success.
The Promise will call the rejectFunction
on any exception thrown,
but it won't call the resolveFunction
automatically when NO exception happens.
How to Promise
You need to implement the executor
function, and at least one of resolve
or reject
.
-
The
executor
function receives two parameters, the first being theresolve
function, the secondreject
. These will be the functions passed topromise.then()
. The asynchronous part of theexecutor
needs to call at leastresolve()
. It also can callreject()
explicitly instead of throwing an exception, this is useful when needing more than one reject-parameters. -
The
resolve
function normally receives one parameter, being what theexecutor
function passes to it, or what the preceding promise'sresolve
orreject
returned. -
The
reject
function normally receives one parameter, being what theexecutor
function passes to it, or the exception thrown byexecutor
.
The executor
function is passed to the Promise
constructor.
That function is executed immediately by the constructor, it is NOT the then()
function which starts it.
On the constructed Promise
you must call then()
, passing
the concrete implementations for resolve
and reject
as parameters.
One of these functions will be called in any case, be it that the asynchronous callback already arrived or not.
In case you get back a Promise from somewhere, call promise.then(resolve, reject)
, and that's it.
Promise Chaining
Chaining is something like promise.then(a, b).then(c, d).then(e, f)
.
I am not sure whether this is really useful, it could easily result in hard-to-read monolithic implementations.
Copy & paste following code into the text-area above and run it. This demonstrates how the return values are passed to the follower-promise.
var makePromise = function(executor, resolve, reject) { return new Promise(executor).then(resolve, reject); }; var followerPromise = makePromise( function(resolve, reject) { if (confirm("Do you want to keep the promise?")) resolve("User decided to keep the promise!"); else reject("Sorry, user decided to NOT keep the promise!"); }, function(resolveParameter) { alert("I am the resolve function: "+resolveParameter); return "This resolve goes to the follower promise"; }, function(error) { alert("I'm the reject function: "+error); return "This reject goes to the follower promise"; } ); followerPromise.then( function(resolveParameter2) { alert("I'm the second resolve function: "+resolveParameter2); }, function(error2) { alert("I'm the second reject function: "+error2); } );
In this implementation, both resolve
and reject
return a value.
The follower promise is used to process these return values, by giving again resolve
and reject
implementations.
But as we can see when pressing "Cancel",
the return of first reject
is passed to second resolve
, not to second reject
- ?
That's the moment where we want to know more about standard behaviour of promises. Being courageous we could trust the A+ specification, but I would recommend to use just the basic features of promises. As I said, we do not always need them. It is just a shortcut. And it could happen that they make life harder, not easier.
Keine Kommentare:
Kommentar veröffentlichen