I thought it was supposed to be possible to chain the .then() method when using ES6 Promises. In other words I thought that when a promise is resolved the value passed to the resolve function should be passed to any chained then handlers. If this is so how come value comes back undefined in the chained then handler below?
function createPromise() {
return new Promise((resolve) => {
resolve(true);
});
}
createPromise()
.then((value) => {
console.log(value); // expected: true, actual: true
})
.then((value) => {
console.log(value); // expected: true, actual: undefined
});
Each then() can return a value that will be used as the resolved value for the next then() call. In your first then(), you don't return anything, hence value being undefined in the next callback. Return value in the first one to make it available in the second.
function createPromise() {
return new Promise((resolve) => {
resolve(true);
});
}
createPromise()
.then((value) => {
console.log(value); // expected: true, actual: true
return value;
})
.then((value) => {
console.log(value); // expected: true, actual: true
});
You have to return it inside a promise to pass it along. Or create a new promise that resolves with it.
createPromise()
.then((value) => {
return value;
})
.then((value) => {
console.log(value);
});
Or
createPromise()
.then((value) => {
return new Promise.resolve(value);
})
.then((value) => {
console.log(value);
});
.then always returns a Promise that resolves to the value returned in the function callback. Since you return nothing in the first call to .then, undefined becomes the resolved value of the Promise returned.
In other words, if you want to resolve true in the second call to .then, you'll have to return it in the first, like so,
createPromise() // returns a Promise
.then(function (value) {
console.log(value); // => true
return value; // now this will return a Promise that resolves the value `true`
}).then(function (value) {
console.log(value) // => true
});
You can refer to MDN's documentation on method chaining for ES2015 promises if you need further information.
Related
Can someone explain pls why the Promise returns pending in the first function, but resolved in the other two? When i read MDN it states that just using the word async wont make the code inside asynchronous (we need to use the word await as well). Also, I return the promise explicitly, since I use resolve() (which should return fulfilled promise as in two other functions, yet it returns Pending in the first function). I provided the code below.
const testFunc = async() => {
return new Promise((resolve,reject)=> {
resolve()
})
}
const testFunc2 = () => {
return new Promise((resolve,reject)=> {
resolve()
})
}
const testFunc3 = () => {
return new Promise((resolve,reject)=> {
resolve()
})
}
testFunc().then(()=> console.log("Hello")).then(()=> console.log("there")).then(()=> console.log("Obi One"))
testFunc2().then(()=> console.log(1)).then(()=> console.log(2)).then(()=> console.log(3))
testFunc3().then(()=> console.log(4)).then(()=> console.log(5)).then(()=> console.log(6))
console.log(testFunc())
console.log(testFunc2())
console.log(testFunc3())
// Results in the console:
// Promise {<pending>}
// Promise {<fulfilled>: undefined}
// Promise {<fulfilled>: undefined}
// 1
// 4
// 2
// 5
// Hello
// 3
// 6
// there
Thats because testFunc (the function with async keyword) is equalevent to this:
const testFunc = () => {
return new Promise((resolve,reject)=> {
resolve(new Promise((r) => {
r()
}))
})
}
and not
const testFunc = () => {
return new Promise((resolve,reject)=> {
resolve()
})
}
Using async queues the executer as a microtask.
Read about it here: https://whistlr.info/2021/async-and-tasks/
The key point:
When you write an asynchronous function, you force the caller to use a microtask to read its result. This is the case even if you don't await inside it—it will return a Promise regardless
My question id related to Javascript promises. The promise constructor allows us to write the logic to resolve or reject. For example
let x = true;
const promise1 = new Promise(function(resolve, reject) {
if (x == true){ // Reject - Resolve logic
resolve('Success!');
}
else {
reject('Reject');
}
});
But now if I chain it with a .then ( () => console.log('Hello')) , how does it get accepted or rejected without provided logic?
promise1.then(() => console.log('Hello') , undefined)
.then(undefined , () => console.log('World'))
.catch( () => console.log('Error'));
My question is:
1. new Promise is either accepted or rejected and then the .then()'s onFullilled or onRejected is called. Also, .then() returns a new Promise. So, what is being promised in the returned promise by .then()?
2. Where do I provide the logic to either resolve or reject the promise returned by .then()? (Like I did in the constructor above)
Also, According to my knowledge - The functions resolve and reject are already present in JS and they mutate the Promise state
Thanks
A picture is worth 1000 words:
function promise1(data) {
return new Promise(function(resolve, reject) {
resolve(++data);
});
}
function promise2(data) {
return new Promise(function(resolve, reject) {
resolve(++data);
});
}
promise1(1)
.then(data => promise2(data))
.then(data => console.log(data));
As you correctly mentioned a promise always returns a new promise. That can be either in pending or resolved state.
Further if it resolves, it can either be rejected with a reason (meaning promise didn't did what it was supposed to do) or resolved with a value (meaning promise successfully completed its task).
let x = true;
const promise1 = new Promise(function(resolve, reject) {
if (x === true){
resolve('Success!');
}
else {
reject('Reject');
}
});
Now, if you use this promise somewhere like,
promise2 = promise1.then(val => {
console.log(val); //logs "Success!"
return "resolved value for promise2"
})
Further,
promise3 = promise2.then(val => {
console.log(val); //logs "resolved value for promise2"
return "some resolved value"
})
Now going back to the constructor function, for x=false,
promise2 = promise1.catch(err => {
console.log(err) //logs "Reject";
return "resolved value from catch block";
})
promise3 = promise2.then(val => {
console.log(val); //logs "resolved value from catch block"
return "some resolved value"
})
or you can throw an error so as to propagate it,
promise2 = promise1.catch(err => {
console.log(err) //logs "Reject";
throw "rejected value from catch";
})
promise3 = promise2.catch(val => {
console.log(val); //logs "rejected value from catch"
throw err;
})
Important part is if you are throwing or returning from catch block?
Read more on docs.
I expected the promise handler to log the promise p1 (not the value "A") since console.log is called with p1 directly. However, it somehow logs "A". How is the promise p1 automatically resolved to "A" without then being called on it ? For example, console.log(p1) does not output "A" directly as is expected. Is something going on behind the scenes ?
var p1 = new Promise(function(resolve, reject) {
resolve("A");
});
var p2 = new Promise(function(resolve, reject) {
resolve(p1);
});
p2.then(function(v) {
console.log(v)
});
EDIT: I understand that calling
p1.then((v) => return v))
returns a new promise that is fulfilled with the value v. Unless Im seriously missing something here, the "p1" reference in the second promise constructor should have been passed directly to console.log, resulting in the block
var p2 = new Promise(function(resolve, reject) {
resolve(p1);
});
p2.then(function(v) {
console.log(v)
});
becoming
console.log(p1).
Since console.log is called directly with p1, NOT the result of p1.then(...), p1 should not be resolved into the value "A" in the same way that printing that a another program
var promise = new Promise(function(resolve, reject) {
resolve("B")
})
console.log(promise)
does not result in the string "B".
EDIT2: I had a misconception that the resolve parameter passed to the executor is a wrapper for unfulfilled function, which caused me tons of confusion. Check out Why does the Promise constructor require a function that calls 'resolve' when complete, but 'then' does not - it returns a value instead? for more details.
From the MDN documentation:
Promise.resolve(value)
Returns a Promise object that is resolved with the given value. If the
value is a thenable (i.e. has a then method), the returned promise
will "follow" that thenable, adopting its eventual state; otherwise
the returned promise will be fulfilled with the value. Generally, if
you don't know if a value is a promise or not, Promise.resolve(value)
it instead and work with the return value as a promise.
p1 is thenable, so the return promise follows it.
Resolving a promise to another promise will automatically make it wait for the other promise's result.
This is what makes promises chainable (returning further promises in then() callbacks).
From MDN Promise().then() return value:
returns an already fulfilled promise, the promise returned by then gets fulfilled with that promise's value as its value.
If handler function return a Promise(), it will auto get the value
I have write an article to explain the return value of handler function more detail
If handler function ...
doesn't return value
var promise = Promise.resolve().then(() => {
// no return;
});
promise.then((value) => {
console.log(promise);
console.log("value =", value);
});
Promise { <state>: "fulfilled", <value>: undefined }
value = undefined
return value that is not Promise
var promise = Promise.resolve().then(() => {
return "value";
});
promise.then((value) => {
console.log(promise);
console.log("value =", value);
});
Promise { <state>: "fulfilled", <value>: "value" }
value = value
return Promise.resolve()
var promise = Promise.resolve().then(() => {
return Promise.resolve("value");
});
promise.then((value) => {
console.log(promise);
console.log("value =", value);
});
Promise { <state>: "fulfilled", <value>: "value" }
value = value
return Promise.reject()
var promise = Promise.resolve().then(() => {
return Promise.reject("value");
});
promise.catch((value) => {
console.log(promise);
console.log("value =", value);
});
Promise { <state>: "rejected", <reason>: "value" }
value = value
return pending Promise()
var promise = Promise.resolve().then(() => {
return new Promise((resolve, reject) => {
resolve("value");
});
});
promise.then((value) => {
console.log(promise);
console.log("value =", value);
});
Promise { <state>: "fulfilled", <value>: "value" }
value = value
throws "error";
var promise = Promise.resolve().then(() => {
throw "error";
});
promise.catch((value) => {
console.log(promise);
console.log("value =", value);
});
Promise { <state>: "rejected", <reason>: "error" }
value = error
Ref: Promise() handler function return value, MDN
How should I stop the promise chain in this case?
Execute the code of second then only when the condition in the first then is true.
var p = new Promise((resolve, reject) => {
setTimeout(function() {
resolve(1)
}, 0);
});
p
.then((res) => {
if(true) {
return res + 2
} else {
// do something and break the chain here ???
}
})
.then((res) => {
// executed only when the condition is true
console.log(res)
})
You can throw an Error in the else block, then catch it at the end of the promise chain:
var p = new Promise((resolve, reject) => {
setTimeout(function() {
resolve(1)
}, 0);
});
p
.then((res) => {
if(false) {
return res + 2
} else {
// do something and break the chain here ???
throw new Error('error');
}
})
.then((res) => {
// executed only when the condition is true
console.log(res)
})
.catch(error => {
console.log(error.message);
})
Demo - https://jsbin.com/ludoxifobe/edit?js,console
You could read the documentation, which says
Promise.then return a rejected Promise if the input function throws an error, or the input function returns a rejected Promise.
If you prefer, you could read the Promise A spec, in the section about then, where promise2 refers to the resulting promise:
If either onFulfilled or onRejected throws an exception e, promise2 must be rejected with e as the reason.)
If you prefer, you could read the excellent 2ality blog:
then() returns a new promise Q (created via the constructor of the receiver):
If either of the reactions returns a value, Q is resolved with it.
If either of the reactions throws an exception, Q is rejected with it.
You could read the brilliant YDKJS:
A thrown exception inside either the fulfillment or rejection handler of a then(..) call causes the next (chained) promise to be immediately rejected with that exception.
You could move the chain into the conditional branch:
p.then((res) => {
if(true) {
return Promise.resolve(res + 2).then((res) => {
// executed only when the condition is true
});
} else {
// do something
// chain ends here
}
});
Just use something like: reject('rejected')
in the else of the first task.
P
.then((res) => {
if(true) {
return res + 2
} else {
reject('rejected due to logic failure' }
})
.then((res) => {
// executed only when the condition is true
console.log(res)
})
Alternatively u can also add a catch section to ur first task with .catch()
Hope this helps.
How should I stop the promise chain in this case?
Execute the code of second then only when the condition in the first then is true.
var p = new Promise((resolve, reject) => {
setTimeout(function() {
resolve(1)
}, 0);
});
p
.then((res) => {
if(true) {
return res + 2
} else {
// do something and break the chain here ???
}
})
.then((res) => {
// executed only when the condition is true
console.log(res)
})
You can throw an Error in the else block, then catch it at the end of the promise chain:
var p = new Promise((resolve, reject) => {
setTimeout(function() {
resolve(1)
}, 0);
});
p
.then((res) => {
if(false) {
return res + 2
} else {
// do something and break the chain here ???
throw new Error('error');
}
})
.then((res) => {
// executed only when the condition is true
console.log(res)
})
.catch(error => {
console.log(error.message);
})
Demo - https://jsbin.com/ludoxifobe/edit?js,console
You could read the documentation, which says
Promise.then return a rejected Promise if the input function throws an error, or the input function returns a rejected Promise.
If you prefer, you could read the Promise A spec, in the section about then, where promise2 refers to the resulting promise:
If either onFulfilled or onRejected throws an exception e, promise2 must be rejected with e as the reason.)
If you prefer, you could read the excellent 2ality blog:
then() returns a new promise Q (created via the constructor of the receiver):
If either of the reactions returns a value, Q is resolved with it.
If either of the reactions throws an exception, Q is rejected with it.
You could read the brilliant YDKJS:
A thrown exception inside either the fulfillment or rejection handler of a then(..) call causes the next (chained) promise to be immediately rejected with that exception.
You could move the chain into the conditional branch:
p.then((res) => {
if(true) {
return Promise.resolve(res + 2).then((res) => {
// executed only when the condition is true
});
} else {
// do something
// chain ends here
}
});
Just use something like: reject('rejected')
in the else of the first task.
P
.then((res) => {
if(true) {
return res + 2
} else {
reject('rejected due to logic failure' }
})
.then((res) => {
// executed only when the condition is true
console.log(res)
})
Alternatively u can also add a catch section to ur first task with .catch()
Hope this helps.