Obserable vs promise
https://morioh.com/p/726c7646cc64The constructor syntax for a promise object is:
let promise = new Promise(function(resolve, reject) {
// executor (the producing code, "singer")
});
new Promise is called the executor.
When the promise is created, this executor function runs automatically.
It contains the producing code, that should eventually produce a
result. In terms of the analogy above: the executor is the “singer”.The resulting
promise object has internal properties:state— initially “pending”, then changes to either “fulfilled” or “rejected”,result— an arbitrary value of your choosing, initiallyundefined.
resolve(value)— to indicate that the job finished successfully:- sets
stateto"fulfilled", - sets
resulttovalue.
- sets
reject(error)— to indicate that an error occurred:- sets
stateto"rejected", - sets
resulttoerror.
- sets
Here’s an example of a Promise constructor and a simple executor function with its “producing code” (the
setTimeout):letpromise=newPromise(function(resolve,reject){// the function is executed automatically when the promise is constructed// after 1 second signal that the job is done with the result "done!"setTimeout(()=>resolve("done!"),1000);});
- The executor is called automatically and immediately (by the
new Promise). - The executor receives two arguments:
resolveandreject— these functions are pre-defined by the JavaScript engine. So we don’t need to create them. Instead, we should write the executor to call them when ready.
resolve("done!") to produce the result:
And now an example of the executor rejecting the promise with an error:
letpromise=newPromise(function(resolve,reject){// after 1 second signal that the job is finished with an errorsetTimeout(()=>reject(newError("Whoops!")),1000);});
resolve or reject to change the state of the corresponding Promise object.The Promise that is either resolved or rejected is called “settled”, as opposed to a “pending” Promise.
There can be only a single result or an error
The executor should call only one
All further calls of
resolve or one reject. The promise’s state change is final.All further calls of
resolve and reject are ignored:let promise = new Promise(function(resolve, reject) {
resolve("done");
reject(new Error("…")); // ignored
setTimeout(() => resolve("…")); // ignored
});
The idea is that a job done by the executor may have only one result or an error.
Also,
Also,
resolve/reject expect only one argument (or none) and will ignore additional arguments.
Reject with
Error objects
In case something goes wrong, we can call
reject with any type of argument (just like resolve). But it is recommended to use Error objects (or objects that inherit from Error). The reasoning for that will soon become apparent.
Immediately calling
resolve/reject
In practice, an executor usually does something asynchronously and calls
resolve/reject after some time, but it doesn’t have to. We also can call resolve or reject immediately, like this:let promise = new Promise(function(resolve, reject) {
// not taking our time to do the job
resolve(123); // immediately give the result: 123
});
For instance, this might happen when we start to do a job but then see that everything has already been completed.
That’s fine. We immediately have a resolved Promise, nothing wrong with that.
That’s fine. We immediately have a resolved Promise, nothing wrong with that.
The
state and result are internal
The properties
state and result of the Promise object are internal. We can’t directly access them from our “consuming code”. We can use the methods .then/.catch/.finally for that. They are described below.Consumers: then, catch, finally
A Promise object serves as a link between the executor (the “producing code” or “singer”) and the consuming functions (the “fans”), which will receive the result or error. Consuming functions can be registered (subscribed) using methods.then, .catch and .finally.then
The most important, fundamental one is.then.The syntax is:
promise.then(function(result){/* handle a successful result */},function(error){/* handle an error */});
.then is a function that:- runs when the Promise is resolved, and
- receives the result.
.then is a function that:- runs when the Promise is rejected, and
- receives the error.
letpromise=newPromise(function(resolve,reject){setTimeout(()=>resolve("done!"),1000);});// resolve runs the first function in .thenpromise.then(result=>alert(result),// shows "done!" after 1 seconderror=>alert(error)// doesn't run);
And in the case of a rejection – the second one:
letpromise=newPromise(function(resolve,reject){setTimeout(()=>reject(newError("Whoops!")),1000);});// reject runs the second function in .thenpromise.then(result=>alert(result),// doesn't runerror=>alert(error)// shows "Error: Whoops!" after 1 second);
.then:letpromise=newPromise(resolve=>{setTimeout(()=>resolve("done!"),1000);});promise.then(alert);// shows "done!" after 1 second
catch
If we’re interested only in errors, then we can usenull as the first argument: .then(null, errorHandlingFunction). Or we can use .catch(errorHandlingFunction), which is exactly the same:letpromise=newPromise((resolve,reject)=>{setTimeout(()=>reject(newError("Whoops!")),1000);});// .catch(f) is the same as promise.then(null, f)promise.catch(alert);// shows "Error: Whoops!" after 1 second
.catch(f) is a complete analog of .then(null, f), it’s just a shorthand.finally
Just like there’s a finally clause in a regulartry {...} catch {...}, there’s finally in promises.The call
.finally(f) is similar to .then(f, f) in the sense that it always runs when the promise is settled: be it resolve or reject.It is a good handler to perform cleanup, e.g. to stop our loading indicators in
finally, as they are not needed any more, no matter what the outcome is.Like this:
newPromise((resolve,reject)=>{/* do something that takes time, and then call resolve/reject */})// runs when the promise is settled, doesn't matter successfully or not.finally(()=>stop loading indicator).then(result=>show result,err=>show error)
-
A
finallyhandler has no arguments. Infinallywe don’t know whether the promise is successful or not. That’s all right, as our task is usually to perform “general” finalizing procedures.
-
Finally passes through results and errors to the next handler.
For instance, here the result is passed throughfinallytothen:
newPromise((resolve,reject)=>{setTimeout(()=>resolve("result"),2000)}).finally(()=>alert("Promise ready")).then(result=>alert(result));// <-- .then handles the result
finally to catch:new Promise((resolve, reject) => {
throw new Error("error");
})
.finally(() => alert("Promise ready"))
.catch(err => alert(err)); // <-- .catch handles the error object
- That’s very convenient, because finally is not meant to process promise results. So it passes them through.
We’ll talk more about promise chaining and result-passing between handlers in the next chapter.
-
The last, but not the least,
.finally(f)is a more convenient syntax than.then(f, f): no need to duplicate the function.
On settled promises handlers runs immediately
If a promise is pending,
.then/catch/finally handlers wait for the result. Otherwise, if a promise has already settled, they execute immediately:// an immediately resolved promise
let promise = new Promise(resolve => resolve("done!"));
promise.then(alert); // done! (shows up right now)
The good thing is:
.then handler is guaranteed to run whether the promise takes time or settles it immediately.Ref:
https://javascript.info/promise-basics
No comments:
Post a Comment