A question asked here before, with the exact same title as this one, was answered with a "You should not use that, use this instead", I am looking to know what it does, not what else could I do, it's about understanding not a simple copy a paste.
My question is quite simple, what is the difference between these three approaches when creating a promise?
const API = (item, fail) =>
new Promise((resolve, reject) => {
if (fail) reject(item + ' ...with an error');
setTimeout(() => resolve(item), 1000);
});
(async () => {
const pro1 = Promise.resolve(API('I am inside resolve'));
const pro2 = Promise.resolve(API('I am inside resolve', true));
const pro3 = Promise.resolve().then(() => API('I am thenable'));
const pro4 = Promise.resolve().then(() => API('I am thenable', true));
const pro5 = new Promise((resolve) => resolve(API('I am a new promise')));
const pro6 = new Promise((resolve) => resolve(API('I am a new promise', true)));
const store = [pro1, pro2, pro3, pro4, pro5, pro6];
const results = await Promise.allSettled(store);
for (const { status, value, reason } of results) {
if (status === 'fulfilled') console.log(value)
else console.log(reason)
}
})();
The difference is in job to be done. While all of this methods are valid, they have different cost and predictability.
Promise.resolve() produces single resolved Promise instance, and depending on value provided to the call JS engine have information to optimize it. It makes all work to be done in a single call to underlying code of the JS engine (usually C++, but could be Java or WASM). So it's always the best choice.
Promise.resolve().then(() => API(/*...*/)) Produce several Promise instances: one at Promise.resolve() and other at .then() call. It also allocates more memory and make several (3 or more) redundant jumps between JS and the engine. It's hardly optimizable and requires intensive heuristics to be performed to figure out is this call optimizable. It's the worst option.
new Promise((resolve) => resolve(API(/* ... */)) allocates one function and one Promise instance and makes two jumps between JS and the engine. It's harder to optimize this call, due to nature of JS.
Promise.resolve().then()
In your examples, the then() makes no difference as you just resolve the promise, and get its data.
then() is typically used to chain promises, take this as an example:
Promise.resolve('foo')
// 1. Receive "foo", concatenate "bar" to it, and resolve that to the next then
.then(function(string) {
return new Promise(function(resolve, reject) {
setTimeout(function() {
string += 'bar';
resolve(string);
}, 1);
});
})
// 2. receive "foobar", register a callback function to work on that string
// and print it to the console, but not before returning the unworked on
// string to the next then
.then(function(string) {
setTimeout(function() {
string += 'baz';
console.log(string); // foobarbaz
}, 1)
return string;
})
Here we chain multiple promises then() off of a previously resolved promise, while maintaining the original data from the first resolve, though in this case we modify it with every new promise.
You can read more about promises and chaining here.
Related
Js is not my first language, by a long chalk. I code in 5 or 6 others, and am comfortable with callbacks, which means that I should be able to grok promises.
However, I inherited a node.js project with something like this (simplified), which makes me uncertain)
let promise = someFunction.then({return xxxx;});
// lots of more code & promises
Promises.all().then(() => {return somethingElse});
I am comfortable with lots of promises and Promises.all() to wait for them all to resolve (although I am replacing all .then with await for cleaner code), BUT ...
that return inside a .then disturbs me. If I understand correctly, .then just wraps an async callback - which could happen at any time, even before the rest of the code and that Promises.all, leaving some of the code unexecuted.
Am I correct to be concerned, or is there something that I am missing?
Tl;dr - is it ok to return inside a .then?
Returning inside .then is extremely common and fine to do. What it does is it produces a Promise that now resolves to the value returned (rather than to the value returned by the previous Promise). Example:
// resolves to 1
const prom1 = Promise.resolve(1);
// chains off of prom1, resolves to 2
const prom2 = prom1.then(() => {
return 2;
});
prom1.then(console.log);
setTimeout(() => {
prom2.then(console.log);
});
This technique is very useful when you need to pass along a value from a prior .then to a subsequent .then.
const makeApiCall = num => Promise.resolve(num + 3);
Promise.resolve(1)
.then((result1) => {
return makeApiCall(result1);
})
.then((result2) => {
console.log(result2);
});
What the return will do in your situation specifically:
let promise = someFunction.then({return xxxx;});
// lots of more code & promises
Promises.all().then(() => {return somethingElse});
If promise is passed to the Promise.all, then its resolve value will be xxxx, rather than whatever someFunction resolves to. (If the Promise.all's .then ignores its argument, then it does nothing except wait for the returned value to resolve, if it's a Promise.)
const someFunction = () => Promise.resolve(1);
let promise = someFunction().then(() => {
return 'xxxx';
});
Promise.all([promise]).then((allResults) => {
console.log('allResults', allResults);
return 'somethingElse';
})
.then((result2) => {
console.log('Next `.then`', result2);
});
Note that your current code has a number of syntax issues you need to correct.
I'd like to accomplish the following using promises: only execute further once the state of something is ready. I.e. like polling for an external state-change.
I've tried using promises and async-await but am not getting the desired outcome. What am I doing wrong here, and how do I fix it?
The MDN docs have something similar but their settimeout is called within the promise--that's not exactly what I'm looking for though.
I expect the console.log to show "This function is now good to go!" after 5 seconds, but instead execution seems to stop after calling await promiseForState();
var state = false;
function stateReady (){
state = true;
}
function promiseForState(){
var msg = "good to go!";
var promise = new Promise(function (resolve,reject){
if (state){
resolve(msg);
}
});
return promise;
}
async function waiting (intro){
var result = await promiseForState();
console.log(intro + result)
}
setTimeout(stateReady,5000);
waiting("This function is now ");
What you're doing wrong is the promise constructor executor function executes immediately when the promise is created, and then never again. At that point, state is false, so nothing happens.
Promises (and async/await) are not a replacement for polling. You still need to poll somewhere.
The good news: async functions make it easy to do conditional code with loops and promises.
But don't put code inside promise constructor executor functions, because of their poor error handling characteristics. They are meant to wrap legacy code.
Instead, try this:
var state = false;
function stateReady() {
state = true;
}
const wait = ms => new Promise(resolve => setTimeout(resolve, ms));
async function promiseForState() {
while (!state) {
await wait(1000);
}
return "good to go!";
}
async function waiting(intro) {
var result = await promiseForState();
console.log(intro + result)
}
setTimeout(stateReady,5000);
waiting("This function is now ");
Based on your comments that you are waiting for messages from a server it appears you are trying to solve an X/Y problem. I am therefore going to answer the question of "how do I wait for server messages" instead of waiting for global variable to change.
If your network API accepts a callback
Plenty of networking API such as XMLHttpRequest and node's Http.request() are callback based. If the API you are using is callback or event based then you can do something like this:
function myFunctionToFetchFromServer () {
// example is jQuery's ajax but it can easily be replaced with other API
return new Promise(function (resolve, reject) {
$.ajax('http://some.server/somewhere', {
success: resolve,
error: reject
});
});
}
async function waiting (intro){
var result = await myFunctionToFetchFromServer();
console.log(intro + result);
}
If your network API is promise based
If on the other hand you are using a more modern promise based networking API such as fetch() you can simply await the promise:
function myFunctionToFetchFromServer () {
return fetch('http://some.server/somewhere');
}
async function waiting (intro){
var result = await myFunctionToFetchFromServer();
console.log(intro + result);
}
Decoupling network access from your event handler
Note that the following are only my opinion but it is also the normal standard practice in the javascript community:
In either case above, once you have a promise it is possible to decouple your network API form your waiting() event handler. You just need to save the promise somewhere else. Evert's answer shows one way you can do this.
However, in my not-so-humble opinion, you should not do this. In projects of significant size this leads to difficulty in tracing the source of where the state change comes form. This is what we did in the 90s and early 2000s with javascript. We had a lot of events in our code like onChange and onReady or onData instead of callbacks passed as function parameters. The result was that sometimes it takes you a long time to figure out what code is triggering what event.
Callback parameters and promises forces the event generator to be in the same place in the code as the event consumer:
let this_variable_consumes_result_of_a_promise = await generate_a_promise();
this_function_generate_async_event((consume_async_result) => { /* ... */ });
From the wording of your question you seem to be wanting to do this instead;
..somewhere in your code:
this_function_generate_async_event(() => { set_global_state() });
..somewhere else in your code:
let this_variable_consumes_result_of_a_promise = await global_state();
I would consider this an anti-pattern.
Calling asynchronous functions in class constructors
This is not only an anti-pattern but an impossibility (as you've no doubt discovered when you find that you cannot return the asynchronous result).
There are however design patterns that can work around this. The following is an example of exposing a database connection that is created asynchronously:
class MyClass {
constructor () {
// constructor logic
}
db () {
if (this.connection) {
return Promise.resolve(this.connection);
}
else {
return new Promise (function (resolve, reject) {
createDbConnection(function (error, conn) {
if (error) {
reject(error);
}
else {
this.connection = conn; // cache the connection
resolve(this.connection);
}
});
});
}
}
}
Usage:
const myObj = new MyClass();
async function waiting (intro){
const db = await myObj.db();
db.doSomething(); // you can now use the database connection.
}
You can read more about asynchronous constructors from my answer to this other question: Async/Await Class Constructor
The way I would solve this, is as follows. I am not 100% certain this solves your problem, but the assumption here is that you have control over stateReady().
let state = false;
let stateResolver;
const statePromise = new Promise( (res, rej) => {
stateResolver = res;
});
function stateReady(){
state = true;
stateResolver();
}
async function promiseForState(){
await stateResolver();
const msg = "good to go!";
return msg;
}
async function waiting (intro){
const result = await promiseForState();
console.log(intro + result)
}
setTimeout(stateReady,5000);
waiting("This function is now ");
Some key points:
The way this is written currently is that the 'state' can only transition to true once. If you want to allow this to be fired many times, some of those const will need to be let and the promise needs to be re-created.
I created the promise once, globally and always return the same one because it's really just one event that every caller subscribes to.
I needed a stateResolver variable to lift the res argument out of the promise constructor into the global scope.
Here is an alternative using .requestAnimationFrame().
It provides a clean interface that is simple to understand.
var serverStuffComplete = false
// mock the server delay of 5 seconds
setTimeout(()=>serverStuffComplete = true, 5000);
// continue until serverStuffComplete is true
function waitForServer(now) {
if (serverStuffComplete) {
doSomethingElse();
} else {
// place this request on the next tick
requestAnimationFrame(waitForServer);
}
}
console.log("Waiting for server...");
// starts the process off
requestAnimationFrame(waitForServer);
//resolve the promise or whatever
function doSomethingElse() {
console.log('Done baby!');
}
I'm using bluebird and I see two ways to resolve synchronous functions into a Promise, but I don't get the differences between both ways. It looks like the stacktrace is a little bit different, so they aren't just an alias, right?
So what is the preferred way?
Way A
function someFunction(someObject) {
return new Promise(function(resolve) {
someObject.resolved = true;
resolve(someObject);
});
}
Way B
function someFunction(someObject) {
someObject.resolved = true;
return Promise.resolve(someObject);
}
Contrary to both answers in the comments - there is a difference.
While
Promise.resolve(x);
is basically the same as
new Promise(function(r){ r(x); });
there is a subtlety.
Promise returning functions should generally have the guarantee that they should not throw synchronously since they might throw asynchronously. In order to prevent unexpected results and race conditions - throws are usually converted to returned rejections.
With this in mind - when the spec was created the promise constructor is throw safe.
What if someObject is undefined?
Way A returns a rejected promise.
Way B throws synchronously.
Bluebird saw this, and Petka added Promise.method to address this issue so you can keep using return values. So the correct and easiest way to write this in Bluebird is actually neither - it is:
var someFunction = Promise.method(function someFunction(someObject){
someObject.resolved = true;
return someObject;
});
Promise.method will convert throws to rejects and returns to resolves for you. It is the most throw safe way to do this and it assimilatesthenables through return values so it'd work even if someObject is in fact a promise itself.
In general, Promise.resolve is used for casting objects and foreign promises (thenables) to promises. That's its use case.
There is another difference not mentioned by the above answers or comments:
If someObject is a Promise, new Promise(resolve) would cost two additional tick.
Compare two following code snippet:
const p = new Promise(resovle => setTimeout(resovle));
new Promise(resolve => resolve(p)).then(() => {
console.log("tick 3");
});
p.then(() => {
console.log("tick 1");
}).then(() => {
console.log("tick 2");
});
const p = new Promise(resolve => setTimeout(resolve));
Promise.resolve(p).then(() => {
console.log("tick 3");
});
p.then(() => {
console.log("tick 1");
}).then(() => {
console.log("tick 2");
});
The second snippet would print 'tick 3' firstly. Why?
If the value is a promise, Promise.resolve(value) would return value exactly. Promise.resolve(value) === value would be true. see MDN
But new Promise(resolve => resolve(value)) would return a new promise which has locked in to follow the value promise. It needs an extra one tick to make the 'locking-in'.
// something like:
addToMicroTaskQueue(() => {
p.then(() => {
/* resolve newly promise */
})
// all subsequent .then on newly promise go on from here
.then(() => {
console.log("tick 3");
});
});
The tick 1 .then call would run first.
References:
http://exploringjs.com/es6/ch_promises.html#sec_demo-promise
So my understanding of promises lead me to believe that my other promises would run one after another in my then chain but I'm doing something wrong here.
The code I'm using currently is as follows
const mainPromise = () => Promise.resolve(
console.log('spinner spinning...')
...spinner code.... //this is omitted code
);
const getData = () => Promise.resolve(
someObj.getProducts('data.json')
);
const updateProduct = () => Promise.resolve(
setTimeout(()=>{
someObj.updateProductHTML()
}, 0)
);
const updateDom = () => {
setTimeout(()=>{
someObj.updateDOM()
}, 0)
};
and my promise chain
mainPromise()
.then(getData)
.then(updateProduct)
.then(updateDom)
;
They seem to be initially running in order but the Ajax call I have in getProducts also has a for loop worker to build my array of objects and is finishing after all my .thens run.
I am attempting to at least have my data call and worker finish before updateProduct and updateDOM runs
--- UPDATE ---
ok so with the revised promises set up as such as per suggestions in the comments and Samanime's answer
const mainPromise = () => Promise.resolve(
console.log('spinner spinning...')
);
const getData = () => new Promise( resolve => {
console.log('getData');
someObj.getProducts('data.json');
resolve();
}
);
const updateProduct = () => new Promise( resolve =>{
console.log('updateProduct');
someObj.updateProductHTML();
resolve();
});
//execute promise chain
mainPromise()
.then(getData)
.then(updateProduct)
.then(page.updateDOM)
;
I updated the promises to not immediately resolve and am attempting to call resolve after I call my functions ( although I'm uneasy as to if resolve will be called before or after these functions ).
Unfortunately, I'm still getting the same behavior. I've added console logs to my functions as well as my promises and I'm getting this list back
log.spinner spinning
log.getData
log.updateProduct
log.A log from the function updateProduct calls
log.48 product objects created (for loop worker in my getProducts function)
log.Data retrieved and assigned
the last two logs would ideally be called after getData
None of the calls or functions outside of the ones provided are return promises, I'm working on legacy code and I'm moving away from the setTimeout trick as well as my results weren't consistent.
--UPDATE 2 --
The problem I'm having is known as Forking/Splitting. I just need to figure out chaining specifically to fix my issue.
-- FINAL --
this is what I ended up working out
// execute promise chain
mainPromise()
.then(getData);
//the timeout is a little hack to ensure the sequence is kept
mainPromise()
.then(() => {
setTimeout(() => {
myObj.updateProductHTML();
myObj.updateDOM();
}, 0);
});
apparently .then(foo).then(bar) just runs foo and bar at the same time
seems to be working ok right but I feel like somethings not right with it still.
I believe it's because Promise.resolve() doesn't do quite what you think it does.
Promise.resolve() creates a new Promise and resolves it immediately using the value of what it's given. Things like setTimeout return their id (an integer) immediately, so they aren't doing what you want. Your getProducts() is probably an async call, so it may be returning null or something as well (if it's returning a Promise or returns the value synchronously, then it's fine).
You're better off writing a normal Promise and calling resolve() at the appropriate time.
const mainPromise = () => Promise.resolve(
console.log('spinner spinning...')
...spinner code....
);
// Assuming it's already returning a Promise or synchronous response. If it isn't, then deal with it like the setTimeout ones below.
const getData = () => someObj.getProducts('data.json')
const updateProduct = () => new Promise(resolve => {
setTimeout(()=>{
someObj.updateProductHTML();
resolve();
}, 0)
});
// You don't NEED to in your example since it's at the end of the chain, but you probably want to wrap this too in case you add to the chain.
const updateDom = () => new Promise(resolve => {
setTimeout(()=>{
someObj.updateDOM();
resolve();
}, 0)
});
I'm using bluebird and I see two ways to resolve synchronous functions into a Promise, but I don't get the differences between both ways. It looks like the stacktrace is a little bit different, so they aren't just an alias, right?
So what is the preferred way?
Way A
function someFunction(someObject) {
return new Promise(function(resolve) {
someObject.resolved = true;
resolve(someObject);
});
}
Way B
function someFunction(someObject) {
someObject.resolved = true;
return Promise.resolve(someObject);
}
Contrary to both answers in the comments - there is a difference.
While
Promise.resolve(x);
is basically the same as
new Promise(function(r){ r(x); });
there is a subtlety.
Promise returning functions should generally have the guarantee that they should not throw synchronously since they might throw asynchronously. In order to prevent unexpected results and race conditions - throws are usually converted to returned rejections.
With this in mind - when the spec was created the promise constructor is throw safe.
What if someObject is undefined?
Way A returns a rejected promise.
Way B throws synchronously.
Bluebird saw this, and Petka added Promise.method to address this issue so you can keep using return values. So the correct and easiest way to write this in Bluebird is actually neither - it is:
var someFunction = Promise.method(function someFunction(someObject){
someObject.resolved = true;
return someObject;
});
Promise.method will convert throws to rejects and returns to resolves for you. It is the most throw safe way to do this and it assimilatesthenables through return values so it'd work even if someObject is in fact a promise itself.
In general, Promise.resolve is used for casting objects and foreign promises (thenables) to promises. That's its use case.
There is another difference not mentioned by the above answers or comments:
If someObject is a Promise, new Promise(resolve) would cost two additional tick.
Compare two following code snippet:
const p = new Promise(resovle => setTimeout(resovle));
new Promise(resolve => resolve(p)).then(() => {
console.log("tick 3");
});
p.then(() => {
console.log("tick 1");
}).then(() => {
console.log("tick 2");
});
const p = new Promise(resolve => setTimeout(resolve));
Promise.resolve(p).then(() => {
console.log("tick 3");
});
p.then(() => {
console.log("tick 1");
}).then(() => {
console.log("tick 2");
});
The second snippet would print 'tick 3' firstly. Why?
If the value is a promise, Promise.resolve(value) would return value exactly. Promise.resolve(value) === value would be true. see MDN
But new Promise(resolve => resolve(value)) would return a new promise which has locked in to follow the value promise. It needs an extra one tick to make the 'locking-in'.
// something like:
addToMicroTaskQueue(() => {
p.then(() => {
/* resolve newly promise */
})
// all subsequent .then on newly promise go on from here
.then(() => {
console.log("tick 3");
});
});
The tick 1 .then call would run first.
References:
http://exploringjs.com/es6/ch_promises.html#sec_demo-promise