Setting a timeout for each promise within a promise.all - javascript

I am able to successfully perform a Promise.all, and gracefully handle resolves and rejects. However, some promises complete within a few milliseconds, some can/could take a while.
I want to be able to set a timeout for each promise within the Promise.all, so it can attempt to take a maximum of say 5seconds.
getData() {
var that = this;
var tableUrls = ['http://table-one.com','http://table-two.com'];
var spoonUrls = ['http://spoon-one.com','http://spoon-two.com'];
var tablePromises = that.createPromise(tableUrls);
var spoonPromises = that.createPromise(spoonUrls);
var responses = {};
var getTableData = () => {
var promise = new Promise((resolve, reject) => {
Promise.all(tablePromises.map(that.rejectResolveHandle))
.then((results) => {
responses.tables = results.filter(x => x.status === 'resolved');
resolve(responses);
});
});
return promise;
};
var getSpoonData = () => {
var promise = new Promise((resolve, reject) => {
Promise.all(spoonPromises.map(that.rejectResolveHandle))
.then((results) => {
responses.tables = results.filter(x => x.status === 'resolved');
resolve(responses);
});
});
return promise;
};
return getTableData()
.then(getSpoonData);
}
rejectResolveHandle() {
return promise.then(function(v) {
return {v:v, status: "resolved"};
}, function(e) {
return {e:e, status: "rejected"};
});
}
createPromise(links) {
var promises = [];
angular.forEach(links, function (link) {
var promise = that._$http({
method: 'GET',
url: link + '/my/end/point',
responseType: 'json'
});
promises.push(promise);
});
return promises;
}
I have tried adding a timeout to createPromise(), however this does not seem to work. Setting a timeout to 300ms, some requests continue for 4+seconds:
createPromise(links) {
var promises = [];
angular.forEach(links, function (link) {
var promise = that._$http({
method: 'GET',
url: link + '/my/end/point',
responseType: 'json'
});
promise = new Promise((resolve) => {
setTimeout(() => {
resolve(promise);
}, 300);
});
promises.push(promise);
});
return promises;
}
I have access to Bluebird if it will makes things easier?

Here's a scheme that creates a Promise.raceAll() function that works kind of like a combination of Promise.all() and Promise.race() where the promises all have a timeout time and value so that if the promise doesn't resolve before that time, it will be short circuited to resolve with the passed in value. This essentially puts each promise into a Promise.race() with a timer. If the timer wins, the promise is resolved with the default value. If the original promise wins, it's resolved with the actual promise result. We use Promise.race() to resolve with the first one to finish (the timeout or the original promise). This is a classic use for Promise.race() (in fact the only practical use I've ever really used it for).
A classic example would be to get me all the results you can in the next 15 seconds. Any results that take longer than 15 seconds, just return null for them and don't wait for them. Here's the code to make this concept work:
Promise.delay = function(t, val) {
return new Promise(resolve => {
setTimeout(resolve.bind(null, val), t);
});
}
Promise.raceAll = function(promises, timeoutTime, timeoutVal) {
return Promise.all(promises.map(p => {
return Promise.race([p, Promise.delay(timeoutTime, timeoutVal)])
}));
}
So, you use Promise.raceAll() like Promise.all() in that you pass it an array of promises, but you also pass it a timeoutTime and a timeoutVal. The timeoutTime is the how long to wait before timing out the promises. The timeoutVal is what to put in the results array for any promise that timed out (often it will be something like null that you can easily recognize as a non-real result).
I'm not sure I entirely what you are doing in your specific code, but here's your links code using the above:
Promise.raceAll(links.map(link => {
return that._$http({
method: 'GET',
url: link + '/my/end/point',
responseType: 'json'
});
}), 5000, null).then(results => {
// process results here
// any timed out values will be null
// you can filter out the timed out results
let final = results.filter(item => !!item);
}).catch(err => {
// process any errors here
});
Or, if you want to make sure Promise.raceAll() gets all results, even if some promises reject, you can add a .catch() handler to each promise:
Promise.raceAll(links.map(link => {
return that._$http({
method: 'GET',
url: link + '/my/end/point',
responseType: 'json'
}).catch(err => {
// don't let Promise.all() see a reject so it gets all results
return null;
});
}), 5000, null).then(results => {
// process results here
// any timed out values will be null
// you can filter out the timed out or rejected results
let final = results.filter(item => !!item);
}).catch(err => {
// process any errors here
});

Your attempt just overwrites promise with the timeout promise, meaning that the HTTP promise will be completely ignored.
This is one of the relatively few places where you'd actually use new Promise*: Create a promise, resolve or reject it based on the HTTP call, and reject it (or resolve it, but that seems odd) on timeout:
createPromise(links) {
return Promise.all(links.map(function(link) {
return new Promise((resolve, reject) => {
that._$http({
method: 'GET',
url: link + '/my/end/point',
responseType: 'json'
})
.then(resolve)
.catch(reject);
setTimeout(() => {
reject(/*...relevant value here...*/); // Seems like reject to me,
// but you could use resolve
// if you prefer
}, 300);
});
}));
}
(That uses map on the assumption that links is an array.)
Note that once resolve or reject has been called for a promise, subsequent calls to either are completely ignored (without error or warning). (If that weren't true, we'd want a flag above to track whether we'd settled the promise already.)
* (Unless you have a promise-enabled version of setTimeout. If you do, you'd use Promise.race like jfriend00 did in their answer.)

One solution might be to convert each promise in an argument array for Promise.all into a timed out promise (say using .map). The timeout function could resolve or reject a supplied argument promise depending on application needs.
Using a unique value to resolve a promise after timeout allows it to be identified in the result array supplied to a Promise.all.then( handler)
This example looks out converting a promise into a resolved promise using a helper function timedPromise. Mapping a promise array to an array of timed out promises is not included.
const timedOutValue = new Error( "promise timeout"); // unique value
const timedPromise = (promise, timeout) => {
return new Promise( (resolve, reject) => {
promise.then(resolve, reject);
setTimeout( resolve, timeout, timedOutValue); // could have used reject
});
};
var pendingPromise = new Promise( (resolve,reject) => null); // never settles
timedPromise(pendingPromise)
.then(
data=> console.log( "pendingPromise resolved with %s",
data === timedOutValue ? "timedOutValue" : data),
reason=> console.log( "pendingPromise rejected with %s",
reason === timedOutValue ? "timedOutValue" : reason)
);

Related

Handle a reject in promise.all() in javascript [duplicate]

I have an array of Promises that I'm resolving with Promise.all(arrayOfPromises);
I go on to continue the promise chain. Looks something like this
existingPromiseChain = existingPromiseChain.then(function() {
var arrayOfPromises = state.routes.map(function(route){
return route.handler.promiseHandler();
});
return Promise.all(arrayOfPromises)
});
existingPromiseChain = existingPromiseChain.then(function(arrayResolved) {
// do stuff with my array of resolved promises, eventually ending with a res.send();
});
I want to add a catch statement to handle an individual promise in case it errors, but when I try, Promise.all returns the first error it finds (disregards the rest), and then I can't get the data from the rest of the promises in the array (that didn't error).
I've tried doing something like ..
existingPromiseChain = existingPromiseChain.then(function() {
var arrayOfPromises = state.routes.map(function(route){
return route.handler.promiseHandler()
.then(function(data) {
return data;
})
.catch(function(err) {
return err
});
});
return Promise.all(arrayOfPromises)
});
existingPromiseChain = existingPromiseChain.then(function(arrayResolved) {
// do stuff with my array of resolved promises, eventually ending with a res.send();
});
But that doesn't resolve.
Thanks!
--
Edit:
What the answers below said were completely true, the code was breaking due to other reasons. In case anyone is interested, this is the solution I ended up with ...
Node Express Server Chain
serverSidePromiseChain
.then(function(AppRouter) {
var arrayOfPromises = state.routes.map(function(route) {
return route.async();
});
Promise.all(arrayOfPromises)
.catch(function(err) {
// log that I have an error, return the entire array;
console.log('A promise failed to resolve', err);
return arrayOfPromises;
})
.then(function(arrayOfPromises) {
// full array of resolved promises;
})
};
API Call (route.async call)
return async()
.then(function(result) {
// dispatch a success
return result;
})
.catch(function(err) {
// dispatch a failure and throw error
throw err;
});
Putting the .catch for Promise.all before the .then seems to have served the purpose of catching any errors from the original promises, but then returning the entire array to the next .then
Thanks!
Promise.all is all or nothing. It resolves once all promises in the array resolve, or reject as soon as one of them rejects. In other words, it either resolves with an array of all resolved values, or rejects with a single error.
Some libraries have something called Promise.when, which I understand would instead wait for all promises in the array to either resolve or reject, but I'm not familiar with it, and it's not in ES6.
Your code
I agree with others here that your fix should work. It should resolve with an array that may contain a mix of successful values and errors objects. It's unusual to pass error objects in the success-path but assuming your code is expecting them, I see no problem with it.
The only reason I can think of why it would "not resolve" is that it's failing in code you're not showing us and the reason you're not seeing any error message about this is because this promise chain is not terminated with a final catch (as far as what you're showing us anyway).
I've taken the liberty of factoring out the "existing chain" from your example and terminating the chain with a catch. This may not be right for you, but for people reading this, it's important to always either return or terminate chains, or potential errors, even coding errors, will get hidden (which is what I suspect happened here):
Promise.all(state.routes.map(function(route) {
return route.handler.promiseHandler().catch(function(err) {
return err;
});
}))
.then(function(arrayOfValuesOrErrors) {
// handling of my array containing values and/or errors.
})
.catch(function(err) {
console.log(err.message); // some coding error in handling happened
});
NEW ANSWER
const results = await Promise.all(promises.map(p => p.catch(e => e)));
const validResults = results.filter(result => !(result instanceof Error));
FUTURE Promise API
Chrome 76: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise/allSettled
You can download https://www.npmjs.com/package/promise.allsettled to get it now. In certain browsers allSettled comes preinstalled with the browser itself. It's worth downloading the package for peace of mind because eg. TypeScript doesn't have default definitions for allSettled.
ES2020 introduces new method for the Promise type: Promise.allSettled().
Promise.allSettled gives you a signal when all the input promises are settled, which means they’re either fulfilled or rejected. This is useful in cases where you don’t care about the state of the promise, you just want to know when the work is done, regardless of whether it was successful.
async function() {
const promises = [
fetch('/api.stackexchange.com/2.2'), // succeeds
fetch('/this-will-fail') // fails
];
const result = await Promise.allSettled(promises);
console.log(result.map(promise => promise.status));
// ['fulfilled', 'rejected']
}
Read more in the v8 blog post.
To continue the Promise.all loop (even when a Promise rejects) I wrote a utility function which is called executeAllPromises. This utility function returns an object with results and errors.
The idea is that all Promises you pass to executeAllPromises will be wrapped into a new Promise which will always resolve. The new Promise resolves with an array which has 2 spots. The first spot holds the resolving value (if any) and the second spot keeps the error (if the wrapped Promise rejects).
As a final step the executeAllPromises accumulates all values of the wrapped promises and returns the final object with an array for results and an array for errors.
Here is the code:
function executeAllPromises(promises) {
// Wrap all Promises in a Promise that will always "resolve"
var resolvingPromises = promises.map(function(promise) {
return new Promise(function(resolve) {
var payload = new Array(2);
promise.then(function(result) {
payload[0] = result;
})
.catch(function(error) {
payload[1] = error;
})
.then(function() {
/*
* The wrapped Promise returns an array:
* The first position in the array holds the result (if any)
* The second position in the array holds the error (if any)
*/
resolve(payload);
});
});
});
var errors = [];
var results = [];
// Execute all wrapped Promises
return Promise.all(resolvingPromises)
.then(function(items) {
items.forEach(function(payload) {
if (payload[1]) {
errors.push(payload[1]);
} else {
results.push(payload[0]);
}
});
return {
errors: errors,
results: results
};
});
}
var myPromises = [
Promise.resolve(1),
Promise.resolve(2),
Promise.reject(new Error('3')),
Promise.resolve(4),
Promise.reject(new Error('5'))
];
executeAllPromises(myPromises).then(function(items) {
// Result
var errors = items.errors.map(function(error) {
return error.message
}).join(',');
var results = items.results.join(',');
console.log(`Executed all ${myPromises.length} Promises:`);
console.log(`— ${items.results.length} Promises were successful: ${results}`);
console.log(`— ${items.errors.length} Promises failed: ${errors}`);
});
Promise.allSettled
Instead of Promise.all use Promise.allSettled which waits for all promises to settle, regardless of the result
let p1 = new Promise(resolve => resolve("result1"));
let p2 = new Promise( (resolve,reject) => reject('some troubles') );
let p3 = new Promise(resolve => resolve("result3"));
// It returns info about each promise status and value
Promise.allSettled([p1,p2,p3]).then(result=> console.log(result));
Polyfill
if (!Promise.allSettled) {
const rejectHandler = reason => ({ status: 'rejected', reason });
const resolveHandler = value => ({ status: 'fulfilled', value });
Promise.allSettled = function (promises) {
const convertedPromises = promises
.map(p => Promise.resolve(p).then(resolveHandler, rejectHandler));
return Promise.all(convertedPromises);
};
}
As #jib said,
Promise.all is all or nothing.
Though, you can control certain promises that are "allowed" to fail and we would like to proceed to .then.
For example.
Promise.all([
doMustAsyncTask1,
doMustAsyncTask2,
doOptionalAsyncTask
.catch(err => {
if( /* err non-critical */) {
return
}
// if critical then fail
throw err
})
])
.then(([ mustRes1, mustRes2, optionalRes ]) => {
// proceed to work with results
})
Using Async await -
here one async function func1 is returning a resolved value, and func2 is throwing a error and returning a null in this situation, we can handle it how we want and return accordingly.
const callingFunction = async () => {
const manyPromises = await Promise.all([func1(), func2()]);
console.log(manyPromises);
}
const func1 = async () => {
return 'func1'
}
const func2 = async () => {
try {
let x;
if (!x) throw "x value not present"
} catch(err) {
return null
}
}
callingFunction();
Output is - [ 'func1', null ]
if you get to use the q library https://github.com/kriskowal/q
it has q.allSettled() method that can solve this problem
you can handle every promise depending on its state either fullfiled or rejected
so
existingPromiseChain = existingPromiseChain.then(function() {
var arrayOfPromises = state.routes.map(function(route){
return route.handler.promiseHandler();
});
return q.allSettled(arrayOfPromises)
});
existingPromiseChain = existingPromiseChain.then(function(arrayResolved) {
//so here you have all your promises the fulfilled and the rejected ones
// you can check the state of each promise
arrayResolved.forEach(function(item){
if(item.state === 'fulfilled'){ // 'rejected' for rejected promises
//do somthing
} else {
// do something else
}
})
// do stuff with my array of resolved promises, eventually ending with a res.send();
});
For those using ES8 that stumble here, you can do something like the following, using async functions:
var arrayOfPromises = state.routes.map(async function(route){
try {
return await route.handler.promiseHandler();
} catch(e) {
// Do something to handle the error.
// Errored promises will return whatever you return here (undefined if you don't return anything).
}
});
var resolvedPromises = await Promise.all(arrayOfPromises);
Promise.allSettled with a filter
const promises = [
fetch('/api-call-1'),
fetch('/api-call-2'),
fetch('/api-call-3'),
];
// Imagine some of these requests fail, and some succeed.
const resultFilter = (result, error) => result.filter(i => i.status === (!error ? 'fulfilled' : 'rejected')).map(i => (!error ? i.value : i.reason));
const result = await Promise.allSettled(promises);
const fulfilled = resultFilter(result); // all fulfilled results
const rejected = resultFilter(result, true); // all rejected results
Have you considered Promise.prototype.finally()?
It seems to be designed to do exactly what you want - execute a function once all the promises have settled (resolved/rejected), regardless of some of the promises being rejected.
From the MDN documentation:
The finally() method can be useful if you want to do some processing or cleanup once the promise is settled, regardless of its outcome.
The finally() method is very similar to calling .then(onFinally, onFinally) however there are couple of differences:
When creating a function inline, you can pass it once, instead of being forced to either declare it twice, or create a variable for it.
A finally callback will not receive any argument, since there's no reliable means of determining if the promise was fulfilled or rejected. This use case is for precisely when you do not care about the rejection reason, or the fulfillment value, and so there's no need to provide it.
Unlike Promise.resolve(2).then(() => {}, () => {}) (which will be resolved with undefined), Promise.resolve(2).finally(() => {}) will be resolved with 2.
Similarly, unlike Promise.reject(3).then(() => {}, () => {}) (which will be fulfilled with undefined), Promise.reject(3).finally(() => {}) will be rejected with 3.
== Fallback ==
If your version of JavaScript doesn't support Promise.prototype.finally() you can use this workaround from Jake Archibald: Promise.all(promises.map(p => p.catch(() => undefined)));
We can handle the rejection at the individual promises level, so when we get the results in our result array, the array index which has been rejected will be undefined. We can handle that situation as needed, and use the remaining results.
Here I have rejected the first promise, so it comes as undefined, but we can use the result of the second promise, which is at index 1.
const manyPromises = Promise.all([func1(), func2()]).then(result => {
console.log(result[0]); // undefined
console.log(result[1]); // func2
});
function func1() {
return new Promise( (res, rej) => rej('func1')).catch(err => {
console.log('error handled', err);
});
}
function func2() {
return new Promise( (res, rej) => setTimeout(() => res('func2'), 500) );
}
const promise1 = Promise.resolve(3);
const promise2 = new Promise((resolve, reject) => setTimeout(reject, 100, 'foo'));
const promises = [promise1, promise2];
let sum = 0;
let promiseErrorArr = [];
Promise.allSettled(promises)
.then((results) => {
results.forEach(result => {
if (result.status === "rejected") {
sum += 1;
promiseErrorArr.push(result)
}
})
return ( (sum>0) ? promiseFailed() : promisePassed())
})
function promiseFailed(){
console.log('one or all failed!')
console.log(promiseErrorArr)
}
function promisePassed(){
console.log('all passed!')
}
// expected output:
// "one or all failed!"
// Array [Object { status: "rejected", reason: "foo" }]
Alternately, if you have a case where you don't particularly care about the values of the resolved promises when there is one failure but you still want them to have run, you could do something like this which will resolve with the promises as normal when they all succeed and reject with the failed promises when any of them fail:
function promiseNoReallyAll (promises) {
return new Promise(
async (resolve, reject) => {
const failedPromises = []
const successfulPromises = await Promise.all(
promises.map(
promise => promise.catch(error => {
failedPromises.push(error)
})
)
)
if (failedPromises.length) {
reject(failedPromises)
} else {
resolve(successfulPromises)
}
}
)
}
You can always wrap your promise returning functions in a way that they catches failure and returning instead an agreed value (e.g. error.message), so the exception won't roll all the way up to the Promise.all function and disable it.
async function resetCache(ip) {
try {
const response = await axios.get(`http://${ip}/resetcache`);
return response;
}catch (e) {
return {status: 'failure', reason: 'e.message'};
}
}
I've found a way (workaround) to do this without making it sync.
So as it was mentioned before Promise.all is all of none.
so... Use an enclosing promise to catch and force resolve.
let safePromises = originalPrmises.map((imageObject) => {
return new Promise((resolve) => {
// Do something error friendly
promise.then(_res => resolve(res)).catch(_err => resolve(err))
})
})
})
// safe
return Promise.all(safePromises)
You would need to know how to identify an error in your results. If you do not have a standard expected error, I suggest that you run a transformation on each error in the catch block that makes it identifiable in your results.
try {
let resArray = await Promise.all(
state.routes.map(route => route.handler.promiseHandler().catch(e => e))
);
// in catch(e => e) you can transform your error to a type or object
// that makes it easier for you to identify whats an error in resArray
// e.g. if you expect your err objects to have e.type, you can filter
// all errors in the array eg
// let errResponse = resArray.filter(d => d && d.type === '<expected type>')
// let notNullResponse = resArray.filter(d => d)
} catch (err) {
// code related errors
}
Not the best way to error log, but you can always set everything to an array for the promiseAll, and store the resulting results into new variables.
If you use graphQL you need to postprocess the response regardless and if it doesn't find the correct reference it'll crash the app, narrowing down where the problem is at
const results = await Promise.all([
this.props.client.query({
query: GET_SPECIAL_DATES,
}),
this.props.client.query({
query: GET_SPECIAL_DATE_TYPES,
}),
this.props.client.query({
query: GET_ORDER_DATES,
}),
]).catch(e=>console.log(e,"error"));
const specialDates = results[0].data.specialDates;
const specialDateTypes = results[1].data.specialDateTypes;
const orderDates = results[2].data.orders;
Unfortunately, I don't have enough reputation to comment (or do much of anything, really), so I'm posting this as an answer in response to Eric's answer here.
The executor function can also be an async function. However, this is usually a mistake, for a few reasons:
If an async executor function throws an error, the error will be lost and won’t cause the newly-constructed Promise to reject. This could make it difficult to debug and handle some errors.
If a Promise executor function is using await, this is usually a sign that it is not actually necessary to use the new Promise constructor, or the scope of the new Promise constructor can be reduced.
From this explanation as to why Promises should not utilize an async executor function
Instead, you should opt for Promise.allSettled(), as suggested here by Asaf.
With the help of allSettled,we can now read the status of
each promise is, and process each error individually, without losing any of this critical information
const promises = [
fetch('/api/first'), // first
fetch('/api/second') // second
];
The simplest way is to handle errors
const [firstResult, secondResult] = await Promise.allSettled(promises)
// Process first
if (firstResult.status === 'rejected') {
const err = firstResult.reason
// Here you can handle error
} else {
const first = firstResult.value
}
// Process second
if (secondResult.status === 'rejected') {
const err = secondResult.reason
// Here you can handle error
} else {
const second = secondResult.value
}
A nice way to handle error
const results = await Promise.allSettled(promises);
const [first, second] = handleResults(results)
function handleResults(results) {
const errors = results.filter(result => result.status === 'rejected').map(result => result.reason)
if (errors.length) {
// Aggregate all errors into one
throw new AggregateError(errors)
}
return results.map(result => result.value)
}
That's how Promise.all is designed to work. If a single promise reject()'s, the entire method immediately fails.
There are use cases where one might want to have the Promise.all allowing for promises to fail. To make this happen, simply don't use any reject() statements in your promise. However, to ensure your app/script does not freeze in case any single underlying promise never gets a response, you need to put a timeout on it.
function getThing(uid,branch){
return new Promise(function (resolve, reject) {
xhr.get().then(function(res) {
if (res) {
resolve(res);
}
else {
resolve(null);
}
setTimeout(function(){reject('timeout')},10000)
}).catch(function(error) {
resolve(null);
});
});
}
I wrote a npm library to deal with this problem more beautiful.
https://github.com/wenshin/promiseallend
Install
npm i --save promiseallend
2017-02-25 new api, it's not break promise principles
const promiseAllEnd = require('promiseallend');
const promises = [Promise.resolve(1), Promise.reject('error'), Promise.resolve(2)];
const promisesObj = {k1: Promise.resolve(1), k2: Promise.reject('error'), k3: Promise.resolve(2)};
// input promises with array
promiseAllEnd(promises, {
unhandledRejection(error, index) {
// error is the original error which is 'error'.
// index is the index of array, it's a number.
console.log(error, index);
}
})
// will call, data is `[1, undefined, 2]`
.then(data => console.log(data))
// won't call
.catch(error => console.log(error.detail))
// input promises with object
promiseAllEnd(promisesObj, {
unhandledRejection(error, prop) {
// error is the original error.
// key is the property of object.
console.log(error, prop);
}
})
// will call, data is `{k1: 1, k3: 2}`
.then(data => console.log(data))
// won't call
.catch(error => console.log(error.detail))
// the same to `Promise.all`
promiseAllEnd(promises, {requireConfig: true})
// will call, `error.detail` is 'error', `error.key` is number 1.
.catch(error => console.log(error.detail))
// requireConfig is Array
promiseAllEnd(promises, {requireConfig: [false, true, false]})
// won't call
.then(data => console.log(data))
// will call, `error.detail` is 'error', `error.key` is number 1.
.catch(error => console.log(error.detail))
// requireConfig is Array
promiseAllEnd(promises, {requireConfig: [true, false, false]})
// will call, data is `[1, undefined, 2]`.
.then(data => console.log(data))
// won't call
.catch(error => console.log(error.detail))
————————————————————————————————
Old bad api, do not use it!
let promiseAllEnd = require('promiseallend');
// input promises with array
promiseAllEnd([Promise.resolve(1), Promise.reject('error'), Promise.resolve(2)])
.then(data => console.log(data)) // [1, undefined, 2]
.catch(error => console.log(error.errorsByKey)) // {1: 'error'}
// input promises with object
promiseAllEnd({k1: Promise.resolve(1), k2: Promise.reject('error'), k3: Promise.resolve(2)})
.then(data => console.log(data)) // {k1: 1, k3: 2}
.catch(error => console.log(error.errorsByKey)) // {k2: 'error'}

JS Promise doesn't chain while working chrome.storage.sync

I'm trying to make a promise chaining, but somehow the asynchronous calling using chrome.storage.sync.get always return later. Here's a correct order I thought it should be:
Create an element and append to body
Call chrome.storage.sync
Based on 2) result, trigger some event on element created in 1)
I've already tried to put new Promise in 2) step, but no successful.
The console.log always return in a wrong order. Something like:
// insertChecbox done
// start promise chrome.storage.sync
// undefined
// result asnyc: ...
Here's my attempt:
const pr = new Promise(resolve => {
this.createCheckbox();
console.log('insertChecbox done')
resolve();
});
pr
.then( resolve => {
console.log("start promise chrome.storage.sync");
return new Promise( resolve => {
return resolve(chrome.storage.sync.get(["code", "isAutocheck"], result => {
console.log('result async: ', result );
return result
}));
});
})
.then( result => {
console.log(result);
console.log('This always run before the `chrome.storage.sync` finishes loading`)
if (result.isAutocheck) {
const domCheckbox = this.getCheckboxElement();
domCheckbox && domCheckbox.click();
}
});
I wanted to separate the result of chrome.storage.sync because I need to work on other stuff, not making this callback too complex
Thanks for noticing this question, I've found an solution by changing a bit in my first .then, by explicitly resolving the result of chrome.storage.sync.get, not the whole thing.
pr
.then( () => {
return new Promise( resolve => {
return chrome.storage.sync.get(["code", "isAutocheck"], result => {
console.log('result async: ', result );
return resolve(result)
});
});
})
.then(...);

Rejecting a Promise by passing a Promise

Why is it exactly that the a resolveing promise correctly waits for the someOtherPromise to complete, but the reject does not? Run the following code sample and check the console.log output. I expected the "myFailingPromise rejected" message to show 2000 ms later, just as the "myPromise resolved" did.
let someOtherPromise = (previousPromise) => {
return new Promise((resolve, reject) => {
setTimeout(() => {
console.log(previousPromise + ' => someOtherPromise after 2000ms');
resolve('someOtherPromise');
}, 2000);
});
}
let myPromise = () => {
return new Promise((resolve, reject) => {
resolve(someOtherPromise('myPromise'));
});
};
let myFailingPromise = () => {
return new Promise((resolve, reject) => {
reject(someOtherPromise('myFailingPromise'));
});
};
myPromise().then((val) => {
// this is executed after `someOtherPromise` resolves.
console.log('myPromise resolved');
}).catch((err) => {
console.log('myPromise rejected');
});
myFailingPromise().then((val) => {
// this is executed after `someOtherPromise` resolves.
console.log('myFailingPromise resolved');
}).catch((err) => {
console.log('myFailingPromise rejected');
});
I know the intended behaviour can be achieved by using someOtherPromise.then(reject) in the second example, but my question is why the promise as an argument to reject is not possible, since it works for resolve.
When you're resolving a promise A, if the value with which you're resolving it is a promise B, then your promise A will match the state of the promise B.
However, when you're rejecting a promise, your only giving a reason, whether the reason looks or not like a promise doesn't matter.
What you need to do, is to resolve with someOtherPromise in both cases.
If you want to wait for the first promise and reject anyway, you can do this:
let myFailingPromise = () => {
return new Promise((resolve, reject) => {
someOtherPromise.then(reject);
});
};
The reject only takes in a reason to highlight the error. Not another promise. You can resolve a promise with another promise of the same type, yet only the last promise's success case you will see.
Have a look at this adapted implementation:
const someOtherPromise = new Promise((resolve, _) => {
resolve("I am a success");
});
const failingPromise = new Promise((_, reject) => {
reject("I failed for a reason");
});
someOtherPromise
.then((result) => {
console.log("some other promise resolves", result);
failingPromise
.then((success) => {
console.log("never called");
})
.catch((reason) => {
console.error("failing promise rejects", reason);
});
}
)
.catch((error) => {
console.error("also never called", error);
});
This is the then-able approach to wait for other promises leading to a callback hell. This is why you can also use async / await syntax:
const app = async () => {
try {
const success1 = await someOtherPromise; // will succeed
console.log(success1);
const success2 = await failingPromise; // never succceds
console.log(success2); // never be reached
} catch (e) {
return Promise.reject(e); // catches the error of failing promise and rethrows it, redundant but here showcases error handling
}
}
;
app()
.then(() => {
console.log("never be reached because of failing promise");
})
.catch(console.error);
In regards to your updated question with the timeout, here is what you could do in order to always await another promise:
const otherPromise = async (willBeSuccesful: boolean) => {
console.log("started timer for case", willBeSuccesful);
return new Promise((resolve, reject) => {
setTimeout(() => {
console.log("resolve timer for case", willBeSuccesful);
const successValue = "Fnord"; // whatever you want
return willBeSuccesful
? resolve(successValue)
: reject("this other promise failed because of reasons"); // only provide a reason, not another promise
});
};
};
const alwaysWaitForOtherPromiseThenRejectAnyway = async (otherPromise) => {
try {
const success = await otherPromise; // always waits 2 seconds, not matter
console.log("other promises succeeded with value", success);
} catch (e) {
return Promise.reject(e); // passing through reason, redundant, only to showcase
}
return Promise.reject("reason why this promise failed"); // only happens after otherPromise was resolved, you could swallow that error and fail here or resolve here as well
};
const succeedingPromise = otherPromise(true);
const failingPromise = otherPromise(false);
alwaysWaitForOtherPromiseThenRejectAnyway(succeedingPromise)
.catch((reason) => console.error(reason)); // fail after waiting for success of other promise
alwaysWaitForOtherPromiseThenRejectAnyway(failingPromise)
.catch((reason) => console.error(reason)); // will fail as soon as otherPromise fails
It will always wait for the timeout before rejection happens. Rejection will have different reasons. Output will be:
started timer for case true
started timer for case false
resolve timer for case true
resolve timer for case false
other promises succeeded with value Fnord
reason why this promise failed
this other promise failed because of reasons

What are some typical use-cases for `Promise.race()`? [duplicate]

As far as I know, there are two options about promise:
promise.all()
promise.race()
Ok, I know what promise.all() does. It runs promises in parallel, and .then gives you the values if both resolved successfully. Here is an example:
Promise.all([
$.ajax({ url: 'test1.php' }),
$.ajax({ url: 'test2.php' })
])
.then(([res1, res2]) => {
// Both requests resolved
})
.catch(error => {
// Something went wrong
});
But I don't understand what does promise.race() is supposed to do exactly? In other word, what's the difference with not using it? Assume this:
$.ajax({
url: 'test1.php',
async: true,
success: function (data) {
// This request resolved
}
});
$.ajax({
url: 'test2.php',
async: true,
success: function (data) {
// This request resolved
}
});
See? I haven't used promise.race() and it behaves like promise.race(). Anyway, is there any simple and clean example to show me when exactly should I use promise.race() ?
As you see, the race() will return the promise instance which is firstly resolved or rejected:
var p1 = new Promise(function(resolve, reject) {
setTimeout(resolve, 500, 'one');
});
var p2 = new Promise(function(resolve, reject) {
setTimeout(resolve, 100, 'two');
});
Promise.race([p1, p2]).then(function(value) {
console.log(value); // "two"
// Both resolve, but p2 is faster
});
For a scenes to be used, maybe you want to limit the cost time of a request :
var p = Promise.race([
fetch('/resource-that-may-take-a-while'),
new Promise(function (resolve, reject) {
setTimeout(() => reject(new Error('request timeout')), 5000)
})
])
p.then(response => console.log(response))
p.catch(error => console.log(error))
With the race() you just need to get the returned promise, you needn't care about which one of the promises in the race([]) firstly returned,
However, without the race, just like your example, you need to care about which one will firstly returned, and called the callback in the both success callback.
I've used it for request batching. We had to batch tens of thousands of records into batches for a long running execution. We could do it in parallel, but didn't want the number of pending requests to get out of hand.
Race lets us keep a fixed number of parallel promises running and add one to replace whenever one completes
const _ = require('lodash')
async function batchRequests(options) {
let query = { offset: 0, limit: options.limit };
do {
batch = await model.findAll(query);
query.offset += options.limit;
if (batch.length) {
const promise = doLongRequestForBatch(batch).then(() => {
// Once complete, pop this promise from our array
// so that we know we can add another batch in its place
_.remove(promises, p => p === promise);
});
promises.push(promise);
// Once we hit our concurrency limit, wait for at least one promise to
// resolve before continuing to batch off requests
if (promises.length >= options.concurrentBatches) {
await Promise.race(promises);
}
}
} while (batch.length);
// Wait for remaining batches to finish
return Promise.all(promises);
}
batchRequests({ limit: 100, concurrentBatches: 5 });
It's a piece to build a timeout system, where:
the request/computation may be canceled by another channel
it will still be used later, but we need an interaction now.
For an example of the second, one might show a spinner "instantly" while still defaulting to show real content if it comes in fast enough. Try running the below a few times - note at least some console message comes "instantly". This might normally be attached to perform operations on a UI.
The key to note is - the result of Promise.race is much less important than the side effects (though, this then is a code smell).
// 300 ms _feels_ "instant", and flickers are bad
function getUserInfo(user) {
return new Promise((resolve, reject) => {
// had it at 1500 to be more true-to-life, but 900 is better for testing
setTimeout(() => resolve("user data!"), Math.floor(900*Math.random()));
});
}
function showUserInfo(user) {
return getUserInfo().then(info => {
console.log("user info:", info);
return true;
});
}
function showSpinner() {
console.log("please wait...")
}
function timeout(delay, result) {
return new Promise(resolve => {
setTimeout(() => resolve(result), delay);
});
}
Promise.race([showUserInfo(), timeout(300)]).then(displayed => {
if (!displayed) showSpinner();
});
Inspiration credit to a comment by captainkovalsky.
An example of the first:
function timeout(delay) {
let cancel;
const wait = new Promise(resolve => {
const timer = setTimeout(() => resolve(false), delay);
cancel = () => {
clearTimeout(timer);
resolve(true);
};
});
wait.cancel = cancel;
return wait;
}
function doWork() {
const workFactor = Math.floor(600*Math.random());
const work = timeout(workFactor);
const result = work.then(canceled => {
if (canceled)
console.log('Work canceled');
else
console.log('Work done in', workFactor, 'ms');
return !canceled;
});
result.cancel = work.cancel;
return result;
}
function attemptWork() {
const work = doWork();
return Promise.race([work, timeout(300)])
.then(done => {
if (!done)
work.cancel();
return (done ? 'Work complete!' : 'I gave up');
});
}
attemptWork().then(console.log);
You can see from this one that the timeout's console.log is never executed when the timeout hits first. It should fail/succeed about half/half, for testing convenience.
Here's an easy example to understand the use of promise.race():
Imagine you need to fetch some data from a server and if the data takes too long to load (say 15 seconds) you want to show an error.
You would call promise.race() with two promises, the first being your ajax request and the second being a simple setTimeout(() => resolve("ERROR"), 15000)
Summary:
Promise.race is a JS built in function that accepts an iterable of Promises (e.g. Array) as an argument. This function then asynchronously returns a Promise as soon as one in of the Promises passed in the iterable is either resolved or rejected.
Example 1:
var promise1 = new Promise((resolve, reject) => {
setTimeout(() => resolve('Promise-one'), 500);
});
var promise2 = new Promise((resolve, reject) => {
setTimeout(() => resolve('Promise-two'), 100);
});
Promise.race([promise1, promise2]).then((value) => {
console.log(value);
// Both resolve, but promise2 is faster than promise 1
});
In this example first an array of Promises is passed in Promise.race. Both of the promises resolve but promise1 resolves faster. Therefore the promise is resolved with the value of promise1, which is the string 'Promise-one'.
Example 2:
const promise1 = new Promise((resolve, reject) => {
setTimeout(() => resolve('succes'), 2000);
});
const promise2 = new Promise((resolve, reject) => {
setTimeout(() => reject('err'), 1000);
});
Promise.race([promise1, promise2])
.then((value) => {
console.log(value);
}).catch((value) => {
console.log('error: ' + value);
});
In this second example the second promise rejects faster than the first promise can resolve. Therefore Promise.race will return a rejected promise with the value of 'err' which was the value that Promise2 rejected with.
The key point to understand is that Promice.race takes an iterable of Promises and returns a Promise based on the first resolved or rejected promise in that iterable (with the corresponding resolve() or reject() values).
Let's take an sample workaround of Promise.race like below.
const race = (promises) => {
return new Promise((resolve, reject) => {
return promises.forEach(f => f.then(resolve).catch(reject));
})
};
You can see race function executes all promises, but whomever finishes first will resolve/reject with wrapper Promise.

Understanding promise.race() usage

As far as I know, there are two options about promise:
promise.all()
promise.race()
Ok, I know what promise.all() does. It runs promises in parallel, and .then gives you the values if both resolved successfully. Here is an example:
Promise.all([
$.ajax({ url: 'test1.php' }),
$.ajax({ url: 'test2.php' })
])
.then(([res1, res2]) => {
// Both requests resolved
})
.catch(error => {
// Something went wrong
});
But I don't understand what does promise.race() is supposed to do exactly? In other word, what's the difference with not using it? Assume this:
$.ajax({
url: 'test1.php',
async: true,
success: function (data) {
// This request resolved
}
});
$.ajax({
url: 'test2.php',
async: true,
success: function (data) {
// This request resolved
}
});
See? I haven't used promise.race() and it behaves like promise.race(). Anyway, is there any simple and clean example to show me when exactly should I use promise.race() ?
As you see, the race() will return the promise instance which is firstly resolved or rejected:
var p1 = new Promise(function(resolve, reject) {
setTimeout(resolve, 500, 'one');
});
var p2 = new Promise(function(resolve, reject) {
setTimeout(resolve, 100, 'two');
});
Promise.race([p1, p2]).then(function(value) {
console.log(value); // "two"
// Both resolve, but p2 is faster
});
For a scenes to be used, maybe you want to limit the cost time of a request :
var p = Promise.race([
fetch('/resource-that-may-take-a-while'),
new Promise(function (resolve, reject) {
setTimeout(() => reject(new Error('request timeout')), 5000)
})
])
p.then(response => console.log(response))
p.catch(error => console.log(error))
With the race() you just need to get the returned promise, you needn't care about which one of the promises in the race([]) firstly returned,
However, without the race, just like your example, you need to care about which one will firstly returned, and called the callback in the both success callback.
I've used it for request batching. We had to batch tens of thousands of records into batches for a long running execution. We could do it in parallel, but didn't want the number of pending requests to get out of hand.
Race lets us keep a fixed number of parallel promises running and add one to replace whenever one completes
const _ = require('lodash')
async function batchRequests(options) {
let query = { offset: 0, limit: options.limit };
do {
batch = await model.findAll(query);
query.offset += options.limit;
if (batch.length) {
const promise = doLongRequestForBatch(batch).then(() => {
// Once complete, pop this promise from our array
// so that we know we can add another batch in its place
_.remove(promises, p => p === promise);
});
promises.push(promise);
// Once we hit our concurrency limit, wait for at least one promise to
// resolve before continuing to batch off requests
if (promises.length >= options.concurrentBatches) {
await Promise.race(promises);
}
}
} while (batch.length);
// Wait for remaining batches to finish
return Promise.all(promises);
}
batchRequests({ limit: 100, concurrentBatches: 5 });
It's a piece to build a timeout system, where:
the request/computation may be canceled by another channel
it will still be used later, but we need an interaction now.
For an example of the second, one might show a spinner "instantly" while still defaulting to show real content if it comes in fast enough. Try running the below a few times - note at least some console message comes "instantly". This might normally be attached to perform operations on a UI.
The key to note is - the result of Promise.race is much less important than the side effects (though, this then is a code smell).
// 300 ms _feels_ "instant", and flickers are bad
function getUserInfo(user) {
return new Promise((resolve, reject) => {
// had it at 1500 to be more true-to-life, but 900 is better for testing
setTimeout(() => resolve("user data!"), Math.floor(900*Math.random()));
});
}
function showUserInfo(user) {
return getUserInfo().then(info => {
console.log("user info:", info);
return true;
});
}
function showSpinner() {
console.log("please wait...")
}
function timeout(delay, result) {
return new Promise(resolve => {
setTimeout(() => resolve(result), delay);
});
}
Promise.race([showUserInfo(), timeout(300)]).then(displayed => {
if (!displayed) showSpinner();
});
Inspiration credit to a comment by captainkovalsky.
An example of the first:
function timeout(delay) {
let cancel;
const wait = new Promise(resolve => {
const timer = setTimeout(() => resolve(false), delay);
cancel = () => {
clearTimeout(timer);
resolve(true);
};
});
wait.cancel = cancel;
return wait;
}
function doWork() {
const workFactor = Math.floor(600*Math.random());
const work = timeout(workFactor);
const result = work.then(canceled => {
if (canceled)
console.log('Work canceled');
else
console.log('Work done in', workFactor, 'ms');
return !canceled;
});
result.cancel = work.cancel;
return result;
}
function attemptWork() {
const work = doWork();
return Promise.race([work, timeout(300)])
.then(done => {
if (!done)
work.cancel();
return (done ? 'Work complete!' : 'I gave up');
});
}
attemptWork().then(console.log);
You can see from this one that the timeout's console.log is never executed when the timeout hits first. It should fail/succeed about half/half, for testing convenience.
Here's an easy example to understand the use of promise.race():
Imagine you need to fetch some data from a server and if the data takes too long to load (say 15 seconds) you want to show an error.
You would call promise.race() with two promises, the first being your ajax request and the second being a simple setTimeout(() => resolve("ERROR"), 15000)
Summary:
Promise.race is a JS built in function that accepts an iterable of Promises (e.g. Array) as an argument. This function then asynchronously returns a Promise as soon as one in of the Promises passed in the iterable is either resolved or rejected.
Example 1:
var promise1 = new Promise((resolve, reject) => {
setTimeout(() => resolve('Promise-one'), 500);
});
var promise2 = new Promise((resolve, reject) => {
setTimeout(() => resolve('Promise-two'), 100);
});
Promise.race([promise1, promise2]).then((value) => {
console.log(value);
// Both resolve, but promise2 is faster than promise 1
});
In this example first an array of Promises is passed in Promise.race. Both of the promises resolve but promise1 resolves faster. Therefore the promise is resolved with the value of promise1, which is the string 'Promise-one'.
Example 2:
const promise1 = new Promise((resolve, reject) => {
setTimeout(() => resolve('succes'), 2000);
});
const promise2 = new Promise((resolve, reject) => {
setTimeout(() => reject('err'), 1000);
});
Promise.race([promise1, promise2])
.then((value) => {
console.log(value);
}).catch((value) => {
console.log('error: ' + value);
});
In this second example the second promise rejects faster than the first promise can resolve. Therefore Promise.race will return a rejected promise with the value of 'err' which was the value that Promise2 rejected with.
The key point to understand is that Promice.race takes an iterable of Promises and returns a Promise based on the first resolved or rejected promise in that iterable (with the corresponding resolve() or reject() values).
Let's take an sample workaround of Promise.race like below.
const race = (promises) => {
return new Promise((resolve, reject) => {
return promises.forEach(f => f.then(resolve).catch(reject));
})
};
You can see race function executes all promises, but whomever finishes first will resolve/reject with wrapper Promise.

Categories

Resources