Why does calling the second function .then(notPromise) still pass the argument to the third function .then(promiseC) even though notPromise() is just a regular function?
I thought only promises can be used with .then() but somehow it still executes (and passes the arguments) properly.
promiseA()
.then(notPromise)
.then(promiseC);
function promiseA() {
return new Promise(function (resolve, reject) {
const string = "a";
resolve(string);
});
}
function notPromise(string) {
const nextString = "b"
const finalString = string + nextString;
return finalString;
}
function promiseC(string) {
return new Promise(function (resolve, reject) {
const nextString = "c";
const finalString = string + nextString;
alert(finalString);
resolve(finalString);
});
}
The then() method returns a Promise.
See docs.
A promise has a handler method. Once a Promise is fulfilled or rejected, the respective handler function will be called asynchronously. The behavior of the handler function follows a specific set of rules as stated here.
Let's go over them one by one. Here is the code we will inspect side by side. Its nothing special, just a chain of promises returning values.
let sequence = new Promise(function (resolve) {
console.log('Say 1')
resolve(1)
})
sequence
.then(() => {
console.log('Say 2')
return 2
})
.then(() => {
console.log('Say 3')
})
.then(() => {
console.log('Say 4')
return Promise.resolve(4)
})
.then(() => {
return new Promise(function (resolve) {
console.log('Say 5')
setTimeout(() => { resolve(5) }, 1000)
})
})
returns a value, the promise returned by then gets resolved with the returned value as its value;
In code, this is Say 2, and your original question. When a value is returned, then() returns a Promise which is resolved with the value your returned.
doesn't return anything, the promise returned by then gets resolved with an undefined value;
same as above.
throws an error, the promise returned by then gets rejected with the thrown error as its value;
same as above, except now then() returns a Promise which is rejected with your error.
returns an already resolved promise, the promise returned by then gets resolved with that promise's value as its value;
In code this is Say 4, where the promise has already been resolved. So now then() returns a Promise which is resolved with the value 4.
returns an already rejected promise, the promise returned by then gets rejected with that promise's value as its value;
same as above, except it now rejects.
returns another pending promise object, the resolution/rejection of the promise returned by then will be subsequent to the resolution/rejection of the promise returned by the handler. Also, the value of the promise returned by then will be the same as the value of the promise returned by the handler.
In code, this is Say 5. If you return a promise which has not been resolved yet, then() will return a Promise with the results of your promise i.e. 5.
One thing to note, that I also actually learned recently (suggested by #Bergi in comments) was that the then() method always constructs and returns a new Promise before the chain callbacks have even started to execute. The callbacks that you pass to then() simply tells the promise the value/error that the promise should resolve/reject with.
In summary, that is why then() chaining works even when you don't specifically return a new Promise - because then() method always constructs a new promise behind the scenes and rejects/resolves that promise with the value you returned. The most complex case in above scenarios is when you return a Promise in your callback, in which case your callback promise's results are passed to the then() promise.
It has to do with the promise chain, it doesn't matter if subsequent calls to then() are not promises, they are all part of the promise chain, the good thing is you can continue chaining promises, which allows you to do several async/promise operations in a row (as your example described), here is a real world example:
// This is the generic http call
private callServer(url: string, method: string, body?: any) {
const uri = env.apiUrl + url;
const session = this.sessionToken;
const headers = {
'Content-Type': 'application/json',
'credentials': 'same-origin',
'x-auth-token': session,
};
return fetch(uri, {
method,
headers,
body: JSON.stringify(body),
})
.then(this.wait) // this is a timer, returns a promise
.then(this.checkStatus) // this is a sync call
.then((r) => r.text()) // r.text() is async
.then((tx) => tx ? JSON.parse(tx) : {}); // this is sync
}
You can read more about the promise chain here
Related
I'm trying to understand how async/await works in conjunction together with promises.
Code
async function latestTime() {
const bl = await web3.eth.getBlock('latest');
console.log(bl.timestamp); // Returns a primitive
console.log(typeof bl.timestamp.then == 'function'); //Returns false - not a promise
return bl.timestamp;
}
const time = latestTime(); // Promise { <pending> }
Issue
As far as I understand, await should be blocking and in the code above it seemingly blocks returning an object bl with the primitive timestamp. Then, my function returns the primitive value, however the time variable is set to a pending promise instead of that primitive. What am I missing?
Async prefix is a kind of wrapper for Promises.
async function latestTime() {
const bl = await web3.eth.getBlock('latest');
console.log(bl.timestamp); // Returns a primitive
console.log(typeof bl.timestamp.then == 'function'); //Returns false - not a promise
return bl.timestamp;
}
Is the same as
function latestTime() {
return new Promise(function(resolve,success){
const bl = web3.eth.getBlock('latest');
bl.then(function(result){
console.log(result.timestamp); // Returns a primitive
console.log(typeof result.timestamp.then == 'function'); //Returns false - not a promise
resolve(result.timestamp)
})
}
An async function always returns a promise. That's how it reports the completion of its asynchronous work. If you're using it in another async function, you can use await to wait for its promise to settle, but in a non-async function (often at the top level or in an event handler), you have to use the promise directly, e.g.:
latestTime()
.then(time => {
console.log(time);
})
.catch(error => {
// Handle/report error
});
...though if you're doing this at the top level of a JavaScript module, all modern environments now support top-level await in modules:
const time = await latestTime();
(Note that if that promise is rejected, your module will fail to load. If your module can work meaningfully even if the promise fails, be sure to wrap that in try/catch to handle promise rejection.)
It might (or might not) throw some light on things to see, in explicit promise callback terms, how the JavaScript engine handles your async function under the covers:
function latestTime() {
return new Promise((resolve, reject) => {
web3.eth.getBlock('latest')
.then(bl => {
console.log(bl.timestamp);
console.log(typeof bl.timestamp.then == 'function');
resolve(bl.timestamp);
})
.catch(reject);
});
}
Some important notes on that:
The function you pass to new Promise (the promise executor function) gets called synchronously by new Promise.
Which is why the operation starts, web3.eth.getBlock is called synchronously to start the work.
Any error (etc.) thrown within the promise executor gets caught by new Promise and converted into a promise rejection.
Any error (etc.) thrown within a promise callback (like the one we're passing then) will get caught and converted into a rejection.
async function will return Promise anyway. Return value will be `Promise, so in your case it will be:
async function latestTime(): Promise<some primitive> {
const bl = await web3.eth.getBlock('latest');
return bl.timestamp;
}
So, further you can use it function like:
const time = await latestTime();
But for achieving general view about async/await feature it will be better to read documentation.
in one of youtube tutorial videos about promises I found following code:
let cleanRoom = function() {
return new Promise((resolve, reject) => {
resolve();
});
};
let removeGarbage = function() {
return new Promise((resolve, reject) => {
resolve();
});
};
let winIcecream = function() {
return new Promise((resolve, reject) => {
resolve();
});
};
cleanRoom().then(function(){
return removeGarbage();
}).then(function() {
return winIcecream();
}).then(function() {
console.log('finished');
})
Why the promises aren't chained like that the every .then word is after the previous promise? I mean, why for example .then is not immediately after removeGarbage() but it is after the cleanRoom().then(), how is it happening that the winIcecream() will run after resolving the removeGarbage promise?
Also do I need to type return declaring every promise like in the code above? If yes, why do I need to do so?
Your initial questions may be answered by rewriting things to variable assignments.
I'm using arrow function syntax to implicitly return the new expression here; if you're using regular functions, then yes, you do have to return the new chained promise if you want to run them in sequence.
const roomCleanedPromise = cleanRoom();
const roomCleanedAndGarbageTakenOutPromise = roomCleanedPromise.then(() => removeGarbage());
const roomCleanedAndGarbageTakenOutAndIcecreamWonPromise = roomCleanedAndGarbageTakenOutPromise.then(() => winIcecream());
const finishedPromise = roomCleanedAndGarbageTakenOutAndIcecreamWonPromise.then(() => console.log('finished'));
However things are easier written using the more modern async/await syntax - the YouTube tutorial you mention is a little outdated, perhaps.
async function cleanRoom() {
console.log('Cleaning room.');
// this could do other async things or just take a while
return {room: 'clean'}; // just to demonstrate a return value
}
async function removeGarbage() {
console.log('Removing garbage.');
// this could do other async things or just take a while
return {garbage: 'removed'};
}
// third function elided for brevity
async function doAllTheThings() {
const roomStatus = await cleanRoom();
const garbageStatus = await removeGarbage();
console.log('finished');
}
The purpose of using a fulfillment handler (the functions passed to then in your example) is to wait for the promise to be fulfilled and then, at that point, do something else.
The goal of that code (apparently) is to wait until the cleanRoom promise is fulfilled, then start the removeGarbage process, and then when that is fulfilled, start the winIcecream process. It's also worth noting that if cleanRoom's promise was rejected instead of being fulfilled, removeGarbage wouldn't happen at all, because it's in a fulfillment handler, not a rejection handler.
If you did this instead:
cleanRoom().then(function() { /*...*/ });
removeGarbage().then(function() { /*...*/ });
winIcecream().then(function() { /*...*/ });
...all three processes would be started immediately and run in parallel (to the extent whatever async process they're modelling can run in parallel with other JavaScript code). There'd be no coordination between them at all.
...how is it happening that the winIcecream() will run after resolving the removeGarbage promise...
then, catch, and finally create and return new promises. Those promises are fulfilled or rejected based on what happens to the promise they were called on and what happens in or is returned by their handler. So for example:
doThis()
.then(function() { return doThat(); })
.then(function() { console.log("done"); });
Let's call the promise from doThis() "Promise A". Calling then on it creates a new promise ("Promise B"), that will either be rejected (if Promise A is rejected) or will call its handler if Promise A is fulfilled. Promise B is resolved to whatever that handler returns. In the code above, suppose Promise A is fulfilled and doThat() returns a promise ("Promise C"). Now, Promise B is resolved to Promise C — whatever happens to Promise C is what will happen to Promise B. If Promise C is fulfilled, Promise B is fulfilled, and the second handler with the console.log is called.
The MDN article on using promises may be helpful.
Your suggestion / thinking I mean, why for example .then is not immediately after removeGarbage() but it is after the cleanRoom().then() is wrong.
Each promise ( .then ) is not execute immediately.
You can take a look at your example ( little edited by me )
const cleanRoom = () => new Promise((resolve, reject) => {
resolve();
});
const removeGarbage = () => new Promise((resolve, reject) => {
resolve();
});
const winIcecream = () => new Promise((resolve, reject) => {
resolve();
})
cleanRoom().then(function(){
console.log('second');
return removeGarbage();
}).then(function() {
return winIcecream();
}).then(function() {
console.log('finished');
})
console.log('first');
You should read more how the event loop works.
As I understand it, resolve and reject are specified in the callback provided to the promise constructor and invoked within the callback. But resolve and reject (I think) always do the same thing, so why do they even have to be specified?
EDIT: I should clarify: I do not think that resolve does the same thing as reject; I mean resolve seems to always resolve and reject always reject -- is there a way to pass in a function instead of resolve that does something else?
EDIT: To further clarify as I did in a comment below: if a Promise constructor takes a function which in turn takes resolve and reject and these are always to be called in the function, why must resolve and reject be specified as arguments at all?
Final Edit: I tried with foo instead of resolve and it still worked. How can that be? I did not define foo at all?? I probably am not understanding something about javascript beyond promises. code snippet where i changed resolve to foo
When you create a JS Promise, you expect some task to happen asynchronously. It is possible, that this task can succeed or fail. The success and failure are defined based on what the task is.
When you create a new Promise, you as the creator must specify what to do when the task is successful or is a failure. To communicate the same to the consumer of the promise resolve/reject come handy.
So when the consumer uses a then to check the output of the promise. what ever is returned by resolve is given to the consumer.
When the consumer uses a catch to check the output of the promise. whatever is returned by the reject is given to the consumer.
import {Promise} from 'es6-promise'
const foo = true;
const bar = new Promise((resolve, reject) => {
if (foo) {
resolve(4);
} else {
reject(2);
}
})
bar.then((value) => console.log(value)).catch((value) => console.log(value));
you can run the above sample by changing the value of foo to true/false and see for yourself what the output is.
I assume you are referring to the then() function which takes two callbacks, one when the promise is resolved and one when it is resolved. The pattern often looks like this:
myPromise.then(result => {
// do something here
},
error => {
}
);
If the promise is resolved everything went as expected and the first callback should proceed as normal. If the promise is rejected the second callback should perform error handling, possibly displaying an appropriate message to the user. These never do the same thing.
If you have logic that should always occur, whether there is an error or not, you should chain another call to then():
myPromise.then(result => {
// do something here
},
error => {
// do something here
}
).then(result => {
// do something here
}
);
This is conceptually the same as try...catch...finally from many programming languages.
Resolve and reject do not "do the same thing".
When you resolve a promise you are saying "this is the successful response of this promise".
When you reject a promise you are saying "this promise failed because...".
For example:
Imagine in the following examples accountApiRequest() actually does an API request to fetch an account. If there is no account it will most likely return null or throw an exception.
When a promise is successful you will resolve it. When a promise is not successful it will be rejected.
Successful promise
function accountApiRequest() {
return {
id: 1234,
username: 'Jim',
}
}
function getAccount() {
return new Promise(function(resolve, reject) {
account = accountApiRequest()
if (!account) {
return reject('Unable to fetch account')
}
return resolve(account)
});
}
getAccount().then(console.log).catch(console.log)
Failed promise
function accountApiRequest() {
return null
}
function getAccount() {
return new Promise(function(resolve, reject) {
account = accountApiRequest()
if (!account) {
return reject('Unable to fetch account')
}
return resolve(account)
});
}
getAccount().then(console.log).catch(console.log)
In the Promise constructor we have a callback which takes the resolve and reject methods as its arguments. After a Promise is created using the constructor a Promise can have three states:
Pending
Resolved
Rejected
When a promise is resolved then the callback which is passed in the then() method is called. If the promise is rejected the callback which is passed in the catch() method is called. For example:
const prom = new Promise((resolve, reject) => {
setTimeout(() => {
if(Math.random() > 0.5) {
resolve('resolved');
} else {
reject('rejected');
}
}
, 2000)
});
prom.then((val) => console.log('succes: ' + val))
.catch((val) => console.log('fail: ' + val))
In our example the resolve or reject is called in a setTimeout, therefore the status of the promise is the first 2 seconds pending. After the setTimeout is expired the Promise is either resolved or rejected based on the value of Math.random(). Then either the then or the catch callback is executed to handle the result of the promise.
When the promise is rejected the callback passed in the catch method is called
When the promise is resolved the callback passed in the then method is called
I had the same question when I read about Promises. And I came to understanding that you don't need to define resolve and reject functions by yourself (they will be ignored and standard resolve/reject functions will be called anyway): I think of them as special functions which let you indicate whether to resolve or reject the promise by calling one of them; and what value to return in each case by passing arguments to these functions. It doesn't matter how you name them in promise constructor - first argument is resolve function and the second - reject.
I have spent a ton of time trying to figure this out. Unfortunately, for anyone well versed with JS syntax, won't really understand the question...
For me, it was clear, after unraveling the concise JS syntactic sugar:
function promise_executor(resolve_callback_provided_by_promise_instance, reject_callback_provided_by_promise_instance) {
if (<some_condition>) {
// success
var response='bla';
resolve_callback_provided_by_promise_instance(response);
} else {
// fail
var error=new Error('failed');
reject_callback_provided_by_promise_instance(error);
}
}
var promise_instance = new Promise(promise_executor);
So the reject and resolve are just random names for callbacks that the promise instance will call.
They have proper interfaces, like both will take 1 param of type object. That param will be passed to either the Promise.then() or the Promise.catch().
resolve
reject
Then, when you use them, it is like this:
function success_callback(param_from_resolve) {
console.log('success() ', param_from_resolve);
}
function fail_callback(param_from_reject) {
console.log('fail(): ', param_from_reject);
}
promise_instance
.then(success_callback) // this will be the var 'resolve' from above
.catch(fail_callback); // this will be the var 'error' from above
The whole code flow is like this:
<at some point, your promise_executor() code is called with 2 functions, as parameters>
// obviously, the promise_executor() function has its own name in the Promise object
promise_executor(Promise.reject, Promise.resolve);
<promise_executor() will -hopefully- call either the resolve or the reject callback, which will in turn trigger either the callback of then() or catch()>
<say we have then()>
<the Promise code will take the response param from resolve_callback_provided_by_promise_instance(response) and give it to the callback from then()>
param_from_resolve(response)
It will go down something like this (THIS IS NOT what actually happens, as in reality, the micro-queue, the eventloop, etc. gets involved - this is a linearized version of the gist of what is happening):
What you see:
var promise = new Promise(promise_executor);
What is executed:
function Promise.constructor(exec_callback_fnc) {
var this = object();
this.exec_callback_fnc = exec_callback_fnc; // this is "promise_executor"
return this;
}
What you see:
// at some point in "this.exec_callback_fnc":
resolve_callback_provided_by_promise_instance(response)
What is executed:
function promise_instance.resolve(param) {
this.param_from_resolve = param;
promise_instance.resolved = true;
}
What you see:
promise_instance
.then(success_callback)
What is executed:
promise_instance.exec_callback_fnc(promise_instance.resolve, promise_instance.reject);
if (promise_instance.resolved) {
function promise_instance.then(callback_fnc) {
callback_fnc(this.param_from_resolve); // this is "success_callback"
}
}
Finally, let's see, how the syntax makes this whole thing more concise:
function promise_executor(resolve_callback_provided_by_promise_instance,
reject_callback_provided_by_promise_instance) {
if (<some_condition>) {
// success
var response='bla';
resolve_callback_provided_by_promise_instance(response);
} else {
// fail
var error=new Error('failed');
reject_callback_provided_by_promise_instance(error);
}
}
var promise_instance = new Promise(promise_executor);
function success_callback(param_from_resolve) {
console.log('success() ', param_from_resolve);
}
function fail_callback(param_from_reject) {
console.log('fail(): ', param_from_reject);
}
promise_instance
.then(success_callback)
.catch(fail_callback);
Will become:
new Promise((resolve_callback_provided_by_promise_instance,
reject_callback_provided_by_promise_instance) => {
if (<some_condition>) {
// success
var response='bla';
resolve_callback_provided_by_promise_instance(response);
} else {
// fail
var error=new Error('failed');
reject_callback_provided_by_promise_instance(error);
}})
// "response" -> "param_from_resolve"
.then((param_from_resolve) => { console.log('success() ', param_from_resolve); })
// "error" -> "param_from_reject"
.catch((param_from_reject) => { console.log('fail(): ', param_from_reject); });
This is tightly coupled to Chaining .then() calls in ES6 promises ...
I tried this with some functions that make up a chain of promises, so basically:
var PromiseGeneratingMethod = function(){
return p = new Promise((resolve, reject) =>{
resolve(1)
});
}
var inBetweenMethod = function(){
return PromiseGeneratingMethod()
.then((resolved) => {
if(resolved){
console.log('resolved in between');
//return resolved
/* this changes output to
resolved in between
resolved at last*/
}else{
console.log('something went terribly wrong in betweeen', resolved);
}
});
}
inBetweenMethod().then((resolved) =>{
if(resolved){
console.log('resolved at last')
}else{
console.log('something went terribly wrong', resolved);
}
})
/* ouput:
resolved in between
something went terribly wrong undefined*/
I don't understand why it is like that. doesn't have a Promise just ONE associated return value? why can I change that value in every then? It seems irrational to me. A Promise Object can only have one return value and I thought every then handler will receive the same parameter after the Promise gets resolved?
This way, having two Methods which call then() on the same Promise, the latter one (in asynchronous environments you never know what that is...) will ALWAYS get an empty result, except if EVERY then returns the desired value
If I got it right, the only good thing is that you can build a then().then().then() chain to make it almost synchronous (by returning arbitrary values in every then()) but you still could achieve the same with nested Promises, right?
Can someone help me understand why es6 Promises work that way and if there are more caveats to using those?
doesn't have a promise just ONE associated return value?
Yes.
why can I change that value in every then?
Because every .then() call does return a new promise.
having two methods which call then() on the same Promise
That's not what you're doing. Your then callbacks are installed on different promises, that's why they get different values.
You could do
function inBetweenMethod() {
var promise = PromiseGeneratingMethod();
promise.then(resolved => { … }); // return value is ignored
return promise;
}
but you should really avoid that. You already noticed that you can get the expected behaviour with
function inBetweenMethod() {
var promise = PromiseGeneratingMethod();
var newPromise = promise.then(value => {
…
return value;
});
return newPromise;
}
where the newPromise is resolved with the value that is returned by the callback - possibly the same value that promise fulfilled with.
you are using .then() handler twice, do the following:
var PromiseGeneratingMethod = function(){
return new Promise((resolve, reject) =>{
if (myCondition) resolve(1)
if (!myCondition) reject("failed")
});
}
var inBetweenMethod = function(){
return PromiseGeneratingMethod()
}
inBetweenMethod().then((resolved) =>{
console.log(resolved)
}).catch(function(err) {
console.log(err)
})
I have 2 functions that resolve a promise and another 3rd constant that is simply an integer. Here I tried Promise.all in order to return promise resolved.
const a = Promise.resolve('First returned');
const b = new Promise((resolve, reject) => {
setTimeout(() => {resolve('second returned');}, 300);
});
const c = 123;
Promise.all([a,b,c]).then(response => {
console.log(response);
});
My question is, since the 3rd constant is simply an integer and doesn't resolve a promise, how it is included in the result. The result I get is ["First returned", "second returned", 123].
If any item in the iterable object which is passed into the Promise is not an instance of the Promise, it will be ignored and passed to the then results using Promise.resolve method. Concise, it will be resolved automatically.
From the Documentation
If the iterable contains non-promise values, they will be ignored, but
still counted in the returned promise array value (if the promise is
fulfilled):
in Promose.all(...), if the iterables are non-promised values, there results will be either
Resolved by Default OR
Result will deduced based promised value
These 3 examples will make it clear
// resolved by default
let a = 100;
let b = 200;
Promise.all([a,b]).then(function(){
console.log("Promised Resolved");
});
Inferred from the results - Resolved
// Resolved here
Promise.all([a,b, Promise.resolve("R-Text")]).then(function{
console.log("R-Test Promise Resolved...");
}).catch(function(text){
console.log("R-Test Promise Rejected...", text);
});
and Inferred from the results - Rejected
// Rejected here
Promise.all([a,b, Promise.resolve("R-Text"), Promise.reject("Rejected")]).then(function{
console.log("R-Test Promise Resolved...");
}).catch(function(text){
console.log("R-Test Promise Rejected...", text);
});