ES6 promise settled callback?
Isn't there a
.always
like jQuery has?
No, there's not (yet). Though there is an active proposal, so maybe ES2018.
Yes, there is: promise .finally()
is part of the standard since ES2018.
If not, how do I achieve this?
You can implement the finally
method yourself like this:
Promise.prototype.finally = function(cb) {
const res = () => this
const fin = () => Promise.resolve(cb()).then(res)
return this.then(fin, fin);
};
or more extensively, with passing resolution information to the callback:
Promise.prototype.finally = function(cb) {
const res = () => this
return this.then(value =>
Promise.resolve(cb({state:"fulfilled", value})).then(res)
, reason =>
Promise.resolve(cb({state:"rejected", reason})).then(res)
);
};
Both ensure that the original resolution is sustained (when there is no exception in the callback) and that promises are awaited.
JavaScript: Promise callback execution
The promise executor function is the function you pass to new Promise
. It is executed synchronously so it can start whatever asynchronous process the promise represents.
The callbacks you attach with then
, catch
, and finally
are always called asynchronously, whether the promise is already settled or not.
So in your example, console.log("After resolving the promise");
is called before the console.log
you've passed to then
.
Here's an example showing that more clearly:
let myPromise = new Promise((resolve, reject) => { console.log("In the executor function"); resolve("Resolved from the promise");});myPromise.then(result => { console.log("In the fulfillment handler: " + result);});
console.log("After resolving the promise");
ES6 Javascript Promise - execute after .then() is called
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.
ES6 Promise.all progress
I've knocked up a little helper function that you can re-use.
Basically pass your promises as normal, and provide a callback to do what you want with the progress..
function allProgress(proms, progress_cb) { let d = 0; progress_cb(0); for (const p of proms) { p.then(()=> { d ++; progress_cb( (d * 100) / proms.length ); }); } return Promise.all(proms);}
function test(ms) { return new Promise((resolve) => { setTimeout(() => { console.log(`Waited ${ms}`); resolve(); }, ms); });}
allProgress([test(1000), test(3000), test(2000), test(3500)], (p) => { console.log(`% Done = ${p.toFixed(2)}`);});
Why do both Promise's then & catch callbacks get called?
The then
callback gets called because the catch
callback is before it, not after. The rejection has already been handled by catch
. If you change the the order (i.e. (promise.then(...).catch(...)
)), the then
callback won't be executed.
MDN says that the .catch()
method "returns a new promise resolving to the return value of the callback". Your catch callback doesn't return anything, so the promise is resolved with undefined
value.
How JavaScript promises work behind the scenes
The following is a simplified implementation of the built-in Promise class. catch
and finally
have not been implemented.
The function supplied to the Promise constructor is called the executor function, and is invoked immediately and synchronously.
Every promise has a method .then
, enabling the chaining of promises.
Functions supplied to .then
are always invoked asynchronously on a microtask (note use of queueMicrotask
below).
Every time .then
is called, a new promise is created and returned.
.then
can be called more than once on the same promise, creating a multicast of the result of the promise, and a branching of the promise chain.
A promise can be in one of three states: pending, fulfilled, or rejected. State transitions are unidirectional: you cannot move from fulfilled or rejected, back to pending.
If a promise is resolved with another promise, then the two promise chains are joined and the outer promise takes on the status of the inner promise (which could be pending), until the inner promise resolves.
function Promise(executor) { if (!executor) throw "Promise executor undefined" let status = "pending", value, thenQ = []
const then = onFulfilled => { let resolver // This ensures control does not move to later promises // until prior promises have been resolved. const nextPromise = new Promise(resolve => (resolver = resolve)) // More than one "then" can be registered with each promise. thenQ.push((...args) => resolver(onFulfilled(...args))) return nextPromise }
// We check if the result is a "thenable"; if so, we treat // it as an inner promise, otherwise we simply fulfil with // the result. const resolve = result => result?.then ? result.then(fulfil) : fulfil(result)
// When a promise has been fulfilled, its "thens" can be run. const fulfil = result => (status = "fulfilled", value = result, executeThens(value))
// "Thens" are run asynchronously, on a microtask. const executeThens = value => queueMicrotask(() => thenQ.forEach(el => el(value)))
// The executor is run synchronously. executor(resolve)
return { then, get status() { return status }, get value() { return value } }}
// Chainingnew Promise(resolve => { console.log('Waiting for step 1...') setTimeout(() => resolve("One, two..."), 1500)}).then(result => new Promise(resolve => { console.log('Waiting for step 2...') setTimeout(() => resolve(`${result}three, four`), 1500)})).then(result => console.log(`Chaining result: ${result}.`))
// Branchingconst p = new Promise(resolve => { console.log('Waiting for step a...') setTimeout(() => resolve("Alpha, Bravo..."), 1500)})
p.then(result => new Promise(resolve => { console.log('Waiting for step b1...') setTimeout(() => resolve(`${result}Charlie, Delta`), 1500)})).then(console.log)
p.then(result => { console.log('Waiting for step b2...') return `${result}Echo, Foxtrot`}).then(console.log)
Resolve Javascript Promise outside the Promise constructor scope
simple:
var promiseResolve, promiseReject;
var promise = new Promise(function(resolve, reject){
promiseResolve = resolve;
promiseReject = reject;
});
promiseResolve();
Why does javascript ES6 Promises continue execution after a resolve?
JavaScript has the concept of "run to completion". Unless an error is thrown, a function is executed until a return
statement or its end is reached. Other code outside of the function can't interfere with that (unless, again, an error is thrown).
If you want resolve()
to exit your initializer function, you have to prepend it by return
:
return new Promise(function(resolve, reject) {
return resolve();
console.log("Not doing more stuff after a return statement");
});
Related Topics
Jquery Duplicate Div into Another Div
How to Remove Youtube Branding After Embedding Video in Web Page
Load JSON from Local File with Http.Get() in Angular 2
Force Page Zoom at 100% with Js
Store Jquery Selector in Variable
How to Detect Escape Key Press with Pure Js or Jquery
Prevent Text Selection After Double Click
How to Inherit from a Class in JavaScript
Write/Add Data in JSON File Using Node.Js
Save a Pre Element as PDF with CSS
How to Reorder Divs Using Flex Box
Get a List of All Folders in Directory
Localstorage Access from Local File
Reformat String Containing Date with JavaScript
Optimum Way to Compare Strings in JavaScript