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
Related
promo()
.then((e) => console.log("finished everything"))
.catch((e) => console.log("failed everything"))
.finally((e) => console.log("done", e));
function promo() {
console.log("awaiting promise");
return new Promise((resolve, reject) => {
setTimeout((e) => {
console.log("done with first promise");
resolve("done");
}, 2000);
}).then((e) => {
console.log("starting second promise");
new Promise((resolve) => {
setTimeout((e) => {
console.log("done with second promise");
resolve("done2");
}, 2000);
});
});
}
how do I make it so promo() doesn't finish until the second .then() is finished? right now it returns it as soon as the first promise ends, and the .then() is triggered after finally();
here is the output:
awaiting promise
done with first promise
starting second promise
finished everything
done
done with second promise
but what I expect is:
awaiting promise
done with first promise
starting second promise
done with second promise
finished everything
done
You're missing a return:
}).then((e) => {
console.log("starting second promise");
//VVVVVV
return new Promise((resolve) => {
setTimeout((e) => {
console.log("done with second promise");
resolve("done2");
}, 2000);
});
});
Any time you call .then on a promise, you create a new promise. If you return a promise in the .then callback, then your new promise will resolve with whatever that returned promise resolves to. If instead you return a non-promise value, then the new promise resolves to that value, with no additional delay. Since you're implicitly returning undefined, your new promise resolves to undefined as soon as the first promise is done.
What is the difference between Promise.any() and Promise.race(), and how do they get used differently?
From MDN,
Also, unlike Promise.race(), which returns the first settled value,
this method returns the first resolved value. This method will ignore
all rejected promises up until the first promise that resolves.
So that brings me to, the difference between resolved and settled. Which then brought me to the MDN promises page, which then brought me to States and Fates
Being settled is not a state, just a linguistic convenience.
So we have Promise.any and Promise.race for linguistic convenience? i.e. there is no difference. Another instance of this equality, is "A promise whose fate is unresolved is necessarily pending." and "We say that a promise is settled if it is not pending, i.e. if it is either fulfilled or rejected.".
So if a promise is resolved, it is not unresolved, so it is not pending. So then, if it's not pending, it's settled. So resolved === settled.
Promise.race and Promise.any do different things:
Promise.race is settled as soon as any of the promises you feed it settle, whether they are fulfilled or rejected.
Promise.any is settled as soon as any of the promises you feed it is fulfilled or they are all rejected, in which case it's rejected with an AggregateError.
The chief differences are:
race's promise is rejected when the first promise you give it is rejected; any's promise isn't, because another promise may be fulfilled instead.
any's promise's rejection reason will be an AggregateError, but race's rejection reason will be the rejection reason from the first promise that was rejected.
So if you pass them both an array of two promises, and one of the promises is rejected, then afterward the other promise is fulfilled, the promise from Promise.race will be rejected (because the first promise to settle was rejected) and the promise from Promise.any will be fulfilled (because although the first promise was rejected, the second was fulfilled). E.g.:
const a = new Promise((_, reject) => setTimeout(reject, 100, new Error("a")));
const b = new Promise((resolve) => setTimeout(resolve, 200, "b"));
Promise.race([a, b]).then(
value => {
console.log(`race: fulfilled with ${value}`);
},
reason => {
console.log(`race: rejected with ${reason.message}`);
}
);
Promise.any([a, b]).then(
value => {
console.log(`any: fulfilled with ${value}`);
},
reason => {
console.log(`any: rejected with ${reason.errors.map(({message}) => message).join()}`);
}
);
With a JavaScript engine that has Promise.any (or a polyfill), that outputs
race: rejected with a
any: fulfilled with b
Play with various outcomes here (there's a very rough incomplete stand-in for Promise.any included if your browser doesn't have it yet):
addFakeAnyIfMissing();
document.querySelector("input[value='Start Again']").addEventListener("click", run);
run();
function setupPromise(name) {
return new Promise((resolve, reject) => {
const div = document.querySelector(`[data-for="${name}"]`);
const btnFulfill = div.querySelector("input[value=Fulfill]");
const btnReject = div.querySelector("input[value=Reject]");;
const display = div.querySelector(".display");
btnFulfill.disabled = btnReject.disabled = false;
display.textContent = "pending";
btnFulfill.onclick = () => {
resolve(name);
display.textContent = `fulfilled with ${name}`;
btnFulfill.disabled = btnReject.disabled = true;
};
btnReject.onclick = () => {
reject(new Error(name));
display.textContent = `rejected with Error(${name})`;
btnFulfill.disabled = btnReject.disabled = true;
};
});
}
function run() {
const a = setupPromise("a");
const b = setupPromise("b");
const raceDisplay = document.querySelector("[data-for=race] .display");
const anyDisplay = document.querySelector("[data-for=any] .display");
raceDisplay.textContent = anyDisplay.textContent = "pending";
Promise.race([a, b]).then(
value => {
raceDisplay.textContent = `fulfilled with ${value}`;
},
reason => {
raceDisplay.textContent = `rejected with ${reason.message}`;
}
);
Promise.any([a, b]).then(
value => {
anyDisplay.textContent = `fulfilled with ${value}`;
},
reason => {
anyDisplay.textContent = `rejected with ${reason.errors.map(({message}) => message).join()}`;
}
);
}
function addFakeAnyIfMissing() {
if (!Promise.any) {
// VERY ROUGH STANDIN, not a valid polyfill
class AggregateError extends Error {}
Object.defineProperty(Promise, "any", {
value(iterable) {
return new Promise((resolve, reject) => {
const errors = [];
let waitingFor = 0;
for (const value of iterable) {
const index = waitingFor++;
Promise.resolve(value).then(
value => {
resolve(value);
--waitingFor;
},
reason => {
errors[index] = reason;
if (--waitingFor === 0) {
reject(Object.assign(new AggregateError(), {errors}));
}
}
);
}
});
},
writable: true,
configurable: true
});
}
}
<div data-for="a">
Promise A
<input type="button" value="Fulfill">
<input type="button" value="Reject">
<span class="display"></span>
</div>
<div data-for="b">
Promise B
<input type="button" value="Fulfill">
<input type="button" value="Reject">
<span class="display"></span>
</div>
<div data-for="race">
<code>Promise.race([a, b])</code>:
<span class="display"></span>
</div>
<div data-for="any">
<code>Promise.any([a, b])</code>:
<span class="display"></span>
</div>
<input type="button" value="Start Again">
This chart from the proposal may help:
There are four main combinators in the Promise landscape.
+−−−−−−−−−−−−−−−−−−−−+−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−+−−−−−−−−−−−−−−−−−+
| name | description | |
+−−−−−−−−−−−−−−−−−−−−+−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−+−−−−−−−−−−−−−−−−−+
| Promise.allSettled | does not short-circuit | Added in ES2020 |
| Promise.all | short-circuits when an input value is rejected | Added in ES2015 |
| Promise.race | short-circuits when an input value is settled | Added in ES2015 |
| Promise.any | short-circuits when an input value is fulfilled | this proposal |
+−−−−−−−−−−−−−−−−−−−−+−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−+−−−−−−−−−−−−−−−−−+
Continuing with your question...
Another instance of this equality, is "A promise whose fate is unresolved is necessarily pending." and "We say that a promise is settled if it is not pending, i.e. if it is either fulfilled or rejected.".
So if a promise is resolved, it is not unresolved, so it is not pending. So then, if its not pending, its settled. So resolved === settled.
I can see how you got there, but you can't invert it like that. :-) A resolved promise can be pending. It's just that an unresolved promise is definitely pending.
The states are:
pending
fulfilled
rejected
You can resolve a promise (A) to another promise (B), which means that while A may still be pending, nothing can change what's going to happen to it; its fate is sealed, it will be fulfilled or rejected according to what happens to B.
(More about this in my blog post Let's talk about how to talk about promises.)
Here's an example of a pending resolved promise:
const b = new Promise((resolve, reject) => {
setTimeout(() => {
if (Math.random() < 0.5) {
resolve("all good");
} else {
reject(new Error("ugh"));
}
}, 100);
});
// (Being verbose for clarity)
const a = new Promise((resolve, reject) => {
resolve(b);
// Now, `a` is pending, but resolved
// No matter what else we do, `a`'s fate is tied to
// `b`'s. For instance, this does nothing:
resolve("foo");
// Neither does this:
reject(new Error("foo"));
});
b
.then(value => {
console.log(`b was fulfilled: ${value}`);
})
.catch(reason => {
console.log(`b was rejected: ${reason.message}`);
});
a
.then(value => {
console.log(`a was fulfilled: ${value}`);
})
.catch(reason => {
console.log(`a was rejected: ${reason.message}`);
});
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.
async function pending() {
return new Promise((resolve, reject) => { resolve(1) });
}
async function fulfilled() {
return 1;
}
function promiseState(p) {
return Promise.race([ Promise.resolve(p).then(() => "fulfilled", () => "rejected"), Promise.resolve().then(() => "pending") ]);
}
promiseState(pending()).then(s => { console.log(s); }); // pending
promiseState(fulfilled()).then(s => { console.log(s); }); // fulfilled
pending().then(r => { console.log(r); }); // 1
fulfilled().then(r => { console.log(r); }); // 1
What's different?
When should I use 'return new Promise(...' in async function? and Why?
It's the same as the difference between
function pending() {
return Promise.resolve(Promise.resolve(1));
}
and
function fulfilled() {
return Promise.resolve(1);
}
The former just takes one promise tick longer to settle.
When should I use 'return new Promise(...' in async function?
Probably never. See How to turn this callback into a promise using async/await? and What is the benefit of prepending async to a function that returns a promise?.
You should not construct Promises explicitly inside another Promise or async function, thats the promise constructor antipattern. It just adds additional overhead and you get no benefit whatsoever. If you somewhen really need the Promise constructor (for wrapping a callback API), then the surounding function should not be async, just return the promise.
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.