I test ES6 Promise in react APP, but i've not result on my .then :
function addItem (value) {
return new Promise((resolve, reject) => {
document.getElementById('todo').innerHTML += `<li>${value}</li>`;
});
}
addItem(value).then(() => {
alert('then !');
}).catch((err) => {
console.log(err)
})
My function is executed, but i've not event on my then.
Do you know where i was wrong ? Thank you ! :)
Promises are used for asynchronous tasks. There's no point in using them to execute synchronous code. In you example, then() is not working because you never resolve the promise. In order to resolve it, you need to call resolve().
You need to call either reject or resolve in your promise.
function addItem (value) {
return new Promise((resolve, reject) => {
document.getElementById('todo').innerHTML += `<li>${value}</li>`;
resolve();
});
}
You need to resolve your promise, for example:
function addItem (value) {
return new Promise((resolve, reject) => {
document.getElementById('todo').innerHTML += `<li>${value}</li>`;
resolve(10);
});
}
addItem(value).then((data) => {
alert('then !');
console.log(data);// 10
}).catch((err) => {
console.log(err)
})
You forgot to call the resolve and reject function in your Promise.
resolve is a function that you call when you're done with your action.
resolve();
The argument that you pass to resolve() is what you get back in the then function.
reject is a function that you call when there's a error with your action. If you don't have anything to reject then you can just leave it out and you won't have to call it.
reject();
the argument that you pass to reject() is what you get back in the catch function.
Related
I have a function that returns a Promise. When I am done using that Promise in the .then() or .catch() blocks, I always want to execute the same cleanup code. My current setup is this:
const promiseWithFinally = () => {
return new Promise((resolve, reject) => {
// resolve or reject at some point
}).finally(() => console.log('finally done'))
}
promiseWithFinally()
.then(() => console.log('then done'))
.catch(() => console.log('catch done'))
What I want to happen is that either then done or catch done get logged first and then finally done. However it seems to get executed in the exact opposite order - when I resolve the Promise after a timeout of 5 seconds finally done gets logged first after 5 seconds and then then done immediately afterwards.
What am I doing wrong or is it possible to do this in general? I know I could just append the .finally() to each individual function call, but since it's always the same I'd like to put it in the function definition.
No it's not possible. Finally is for cleaning up after a given promise, not for its then or catch methods.
What you can do is pass then and catch methods to the function which will be appended before finally:
const promiseWithFinally = (chain) => {
return new Promise((resolve, reject) => {
// resolve or reject at some point
setTimeout(resolve, 1000);
}).then(chain.then, chain.catch).finally(() => console.log('finally done'))
}
promiseWithFinally({
then: () => console.log('then done'),
catch: () => console.log('catch done')
})
Short answer
No it is not possible as you cannot rely on when finally is being ran.
Longer answer and possible solution
Code
const cleanupFunc = () => {
console.log('Cleaning up.');
};
const someAsyncMethod = (thenFunc, catchFunc) => {
new Promise(
(resolve, reject) => {
setTimeout(() => resolve(), 5000);
},
)
.then((...args) => {
try {
thenFunc(...args);
} catch (err) {
}
cleanupFunc();
})
.catch((...args) => {
try {
catchFunc(...args);
} catch (err) {
}
cleanupFunc();
});
};
someAsyncMethod((result) => console.log('Then done'), (err) => console.log('Catch done'));
Explanation
Al though it is not possible to rely on finally, what you can do is write a function that needs to do some asynchronous operation returning a promise. In my example this operation is waiting on a 5 second timeout but his can also, for example, be an asynchronous api call that returns a promise.
The next step would be to add a then and a catch call to the promise the asynchronous operation returns that both begin with a try clause in which you call the callback parameter that belongs to the type of resolve (thenFunc for then, catchFunc for catch) followed by a catch which doesn't do anything and ends by calling the cleanup function. in this way you are certain that whatever happens during running the then or catch callback, the cleanup function is being called.
Assuming you know the rest of the handlers to that promise are going to be attached synchronously, and all actions in the handler are synchronous, it is possible, albeit a little bit hacky.
Simply have the finally handler re-attach itself at the end:
const promiseWithFinally = () => {
const thisPromise = new Promise((resolve, reject) => {
// Rejection example
setTimeout(reject, 200);
}).finally(() => {
setTimeout(() => {
thisPromise.finally(() => console.log('finally done')).catch(() => {});
}, 0);
});
return thisPromise;
};
promiseWithFinally()
.then(() => console.log('then done'))
.catch(() => console.log('catch done'));
I'm given an array of promises that I'm passing to Promise.all, but I was getting Uncaught (in promise) Error and I couldn't understand why. Trying to reproduce the error I've come to the following minimal example.
Promise
.all([
new Promise(async () => {
throw new Error('thrown');
}),
])
.then(results => {
console.log('results', results);
})
.catch(error => {
console.log('error', error);
})
Running that you will see an "Uncaught" message in your console.
The example is fixable by removing the async keyword. But, assuming I'm given the promises from a library I don't have control over, how do I make this work?
Another way to "fix" it is by rewriting it like this
Promise
.all([
new Promise(async (resolve, reject) => {
reject(new Error('rejected'));
}),
])
.then(results => {
console.log('results', results);
})
.catch(error => {
console.log('error', error);
})
Why does this work differently from the throw version? I thought the throw would be handled as a rejection.
EDIT:
Thanks to #CertainPerformance's answer below, my example can be simplified even further, like this
Promise
.all([
new Promise(async () => {
throw new Error('thrown');
}),
])
.then(() => console.log('resolve'))
.catch(() => console.log('reject'));
And then getting rid of Promise.all altogether like so
new Promise(async () => {
throw new Error('thrown');
})
.then(() => console.log('resolved'))
.catch(() => console.log('rejected'));
And since async is pretty much only syntactic sugar around promises, the above can be rewritten like this, and still show the same behavior
new Promise(() => {
return new Promise(() => {
throw new Error('thrown')
});
})
.then(() => console.log('resolved'))
.catch(() => console.log('rejected'));
throw inside a promise rejects that promise, so the above can be rewritten like this
new Promise(() => {
return new Promise((resolve, reject) => {
reject('instead of throw')
});
})
.then(() => console.log('resolved'))
.catch(() => console.log('rejected'));
And all that does, is immediately rejects the promise, and we have an easier way to do that:
new Promise(() => {
return new Promise.reject('instead of throw');
})
.then(() => console.log('resolved'))
.catch(() => console.log('rejected'));
And with that I think I fully understand why things are working the way they do.
EDIT 2:
Here's a longer example that more realistically mimics my actual code
function textPromise() {
return new Promise(resolve => {
setTimeout(() => {
resolve('data from fetch');
}, 2000);
});
}
function fetch(ok) {
return Promise.resolve({
ok,
text: textPromise(),
});
}
async function handleResponse(response) {
const data = response.text;
if (!response.ok) {
const resolvedData = await data;
throw new Error(resolvedData);
}
return data;
}
function post(ok) {
const promise = fetch(ok)
.then(response => handleResponse(response));
return promise;
}
const validatorPromise = new Promise(resolve => {
post(false).then(response => {
if (response === 'data from fetch') {
resolve('validated OK');
} else {
resolve('validated NOK');
}
});
});
Promise
.all([validatorPromise])
.then(text => console.log('text', text))
.catch(() => console.log('reject'));
That example can most certainly be simplified, and I will try to do that. But it shows the same problem I tried to show in my original example.
The only code I have control over for now is the last Promise.all stuff. I'm given an array of "validator promises", and I need to handle when something in there throws an error
EDIT 3:
And here's a simplified version of the example above, but that still retains the async function
async function handleResponse(response) {
throw new Error('resolvedData');
}
function post() {
return Promise.resolve('').then(handleResponse);
}
const validatorPromise = new Promise(resolve => {
post().then(() => resolve('validated OK'));
});
validatorPromise
.then(text => console.log('text', text))
.catch(() => console.log('reject'));
(The async function will be wrapped in a promise by the JS interpreter, so essentially we will still get new Promise(async ..., but it's not as obvious here)
When you pass a function to new Promise, that function is called immediately with resolve and reject parameters. If the function throws synchronously (that is, the function throws before synchronously completing), the Promise will reject, without requiring the resolve or reject parameters (if used) to be called.
When the async function is invoked, it will return a rejected Promise, but it'll still return something. In contrast, a synchronous function which throws an error will not return at all; execution stops, and the Promise.all knows that since execution stopped during execution that it should look for a .catch handler and invoke it.
When neither resolve nor reject parameters are called, and the function doesn't appear to the Promise.all to throw synchronously (in this case, the async function returns a Promise, rather than failing entirely), the Promise will remain unresolved forever. That's what's happening in your first snippet, so neither the .then nor the .catch chained off the Promise.all run.
The same behavior is exhibited with a plain Promise, without the Promise.all:
// Will remain unresolved forever, because the constructor callback successfully completes
// (and returns a rejected Promise, which goes unused)
new Promise(async () => {
throw new Error('thrown');
})
.then(() => console.log('resolved'))
.catch(() => console.log('rejected'));
// Will throw synchronously, and therefore reject:
new Promise(() => {
throw new Error('thrown');
})
.then(() => console.log('resolved'))
.catch(() => console.log('rejected'));
The reasonable thing to do is: invoke the Promise constructor with logical paths that always eventually lead to either its resolve or reject parameter being called. (Or you can throw an error, if the Promise constructor callback isn't async)
Given the new code, which can be reduced to:
const post = ok => ok ? Promise.resolve() : Promise.reject();
const validatorPromise = new Promise(resolve => {
post(false).then(response => {
if (response === 'data from fetch') {
resolve('validated OK');
} else {
resolve('validated NOK');
}
});
});
Promise
.all([validatorPromise])
.then(text => console.log('text', text))
.catch(() => console.log('reject'));
The validatorPromise function is broken. It does not catch errors from post - it only works if post does not reject. If you can't change validatorPromise (nor anything it depends on), the library you're using can be considered to be broken, because it does not handle errors. Post an issue on its github, or fork the library and fix it yourself.
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.
If you want to execute code after a callback is called in JavaScript, then you can just place it after the callback:
function demoCallback(callback) {
callback()
console.log("I execute second")
}
demoCallback(() => {
console.log("I execute first")
})
Is it possible to do the same thing with an ES6 Promise from within the scope of the function? Let's say I have a function that returns a Promise:
function demoPromise() {
return new Promise((resolve, reject) => {
resolve()
console.log("I execute first")
})
}
demoPromise().then(() => { console.log("I execute second") })
The Code inserted after resolve executes once the Promise is resolved, but before then is called outside the scope of the function. Is there a way that I can execute code after both, but do so from within the scope of the function?
Is it possible to do the same thing with an ES6 Promise from within the scope of the function?
No, this isn't possible. then callbacks always run asynchronously, and that includes being asynchronous in regard to the resolve() call.
(That said, promise callbacks are queued, so you can abuse that queue to get your code behind the other:
function demoPromise() {
const p = new Promise((resolve, reject) => {
setTimeout(() => {
resolve();
p.then(() => {
console.log("I execute second");
}); // because I will be scheduled second
}, 1000);
});
return p;
}
demoPromise().then(() => {
console.log("I execute first");
}); // because I was scheduled first
But please don't do that)
If you want to execute code after a callback is called in JavaScript
then you probably should not just return a promise. Take a callback that does what you want before executing your code:
function demoPromise(callback) {
return new Promise((resolve, reject) => {
setTimeout(resolve, 1000);
}).then(() => {
return callback();
}).then(res => {
console.log("I execute second");
});
}
demoPromise(() => {
console.log("I execute first");
}).then(() => {
console.log("I execute last");
});
This is known as the disposer pattern and very useful for handling resources.
You can resolve the Promise with a function that can be called in a then block. So long as that function is called within the block, you can execute code before it like so
function demoPromise() {
return new Promise((resolve, reject) => {
resolve(function(){
console.log("I execute second")
});
})
}
demoPromise().then(f => { console.log("I execute first");f();})
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.