I am currently trying to do a get request in my NodeJS API, get some data and return the modified value.
From what I read in other similar questions is that you cannot just return the modified object but you need to use a callback function or a promise in order to return it. I have a standard MVC pattern where I use a controller, service.
Here is my service:
const rp = require('request-promise');
exports.RequestUserPermissions = async function(role, next) {
try {
await rp('https://api.myjson.com/bins/7jau8').then(response => {
const permissionsResponse = JSON.parse(response);
const filteredPermissions = permissionsResponse.find(function(x) {
return Object.keys(x).indexOf(role) > -1;
});
console.log(filteredPermissions); // I GET UNDEFINED HERE.
return filteredPermissions;
});
} catch (error) {
console.log(error);
next(error);
}
};
Here is my controller:
const UserPermissionsService = require('../services/userPermissions.service');
exports.getUserPermissions = async function(req, res) {
try {
const role = req.headers.role; // console.log(req.headers.role);
const loggedInUserPermissions = await UserPermissionsService.RequestUserPermissions(role);
return res.status(200).json({ status: 200, data: loggedInUserPermissions, message: 'User permissions retrieved.' });
} catch (error) {
throw Error(error, 'error inside the get.user.permissions function');
}
};
So my issue is that I'm trying to return the value of filteredPermissions to my controller but I keep getting undefined. Which I guess it's a async - await issue. Meaning that the function ends before I make my calculations.
I originally had my service as:
await request.get('https://api.myjson.com/bins/7jau8', (error, response, body) => {
if (!error && response.statusCode === 200) {
const permissionsResponse = JSON.parse(body);
const filteredPermissions = permissionsResponse.find(function(x) {
return Object.keys(x).indexOf(role) > -1;
});
return permissionsResponse;
} else {
console.log('Got an error:', error);
}
});
but I changed it to use the request-promise module, so that I can return my response. What am I doing wrong ? How can I pass my calculations properly??
Change this:
await rp('https://api.myjson.com/bins/7jau8')
to this:
return rp('https://api.myjson.com/bins/7jau8')
You need to be returning something useful from your exports.RequestUserPermissions function. As it stands now, there's no return value from that function which means the promise it returns will just have an undefined resolved value which is apparently what you are experiencing.
Then, I'd suggest using a .catch() for the error condition. And, you need to allow the caller to see the error (probably as a rejected promise) so it can know when there's an error.
I would suggest this:
const rp = require('request-promise');
exports.RequestUserPermissions = function(role, next) {
return rp('https://api.myjson.com/bins/7jau8').then(response => {
const permissionsResponse = JSON.parse(response);
const filteredPermissions = permissionsResponse.find(function(x) {
return Object.keys(x).indexOf(role) > -1;
});
console.log(filteredPermissions); // I GET UNDEFINED HERE.
return filteredPermissions;
}).catch(error => {
console.log(error);
next(error);
throw error;
});
};
The spec for exactly what you want is a bit confused. To be able to test things with the URL you gave me, I created a simple stand-alone node program here. This looks for one matching role and returns that. If no matching role is found, it resolves to null. You could also make that reject, depending upon how the caller wants no matching role to work.
const rp = require('request-promise');
function getRole(role) {
return rp({uri: "https://api.myjson.com/bins/7jau8", json: true}).then(data => {
// need to find the matching role
// apparently role can be a string or an array of strings
for (let item of data) {
if (item[role]) {
return item[role];
}
}
return null;
});
}
getRole("admin").then(data => {
console.log(data);
}).catch(err => {
console.log(err);
});
When, I run this, I get this output:
{ static:
[ 'posts:list',
'posts:create',
'posts:edit',
'posts:delete',
'users:get',
'users:getSelf',
'home-page:visit',
'dashboard-page:visit' ]
}
Hopefully, can you take this an modify to fit your other needs.
Note: I'm using the json:true option with rp() so it will parse the JSON response for me automatically.
If you are using async/await with request-promise then you don't need to call .then(), you can just assign your rp call directly to a variable. For example this:
await rp('https://api.myjson.com/bins/7jau8').then(response => {
const permissionsResponse = JSON.parse(response);
const filteredPermissions = permissionsResponse.find(function(x) {
return Object.keys(x).indexOf(role) > -1;
});
console.log(filteredPermissions); // I GET UNDEFINED HERE.
return filteredPermissions;
});
Would become this:
const response = await rp('https://api.myjson.com/bins/7jau8');
const permissionsResponse = JSON.parse(response);
const filteredPermissions = permissionsResponse.find(function(x) {
return Object.keys(x).indexOf(role) > -1;
});
console.log(filteredPermissions); // I GET UNDEFINED HERE.
return filteredPermissions;
I'm trying to have a loop with some db calls, and once their all done ill send the result. - Using a promise, but if i have my promise after the callback it dosent work.
let notuser = [];
let promise = new Promise((resolve, reject) => {
users.forEach((x) => {
User.find({
/* query here */
}, function(err, results) {
if(err) throw err
if(results.length) {
notuser.push(x);
/* resolve(notuser) works here - but were not done yet*/
}
})
});
resolve(notuser); /*not giving me the array */
}).then((notuser) => {
return res.json(notuser)
})
how can i handle this ?
Below is a function called findManyUsers which does what you're looking for. Mongo find will return a promise to you, so just collect those promises in a loop and run them together with Promise.all(). So you can see it in action, I've added a mock User class with a promise-returning find method...
// User class pretends to be the mongo user. The find() method
// returns a promise to 'find" a user with a given id
class User {
static find(id) {
return new Promise(r => {
setTimeout(() => r({ id: `user-${id}` }), 500);
});
}
}
// return a promise to find all of the users with the given ids
async function findManyUsers(ids) {
let promises = ids.map(id => User.find(id));
return Promise.all(promises);
}
findManyUsers(['A', 'B', 'C']).then(result => console.log(result));
I suggest you take a look at async it's a great library for this sort of things and more, I really think you should get used to implement it.
I would solve your problem using the following
const async = require('async')
let notuser = [];
async.forEach(users, (user, callback)=>{
User.find({}, (err, results) => {
if (err) callback(err)
if(results.length) {
notUser.push(x)
callback(null)
}
})
}, (err) => {
err ? throw err : return(notuser)
})
However, if you don't want to use a 3rd party library, you are better off using promise.all and await for it to finish.
EDIT: Remember to install async using npm or yarn something similar to yarn add async -- npm install async
I used #danh solution for the basis of fixing in my scenario (so credit goes there), but thought my code may be relevant to someone else, looking to use standard mongoose without async. I want to gets a summary of how many reports for a certain status and return the last 5 for each, combined into one response.
const { Report } = require('../../models/report');
const Workspace = require('../../models/workspace');
// GET request to return page of items from users report
module.exports = (req, res, next) => {
const workspaceId = req.params.workspaceId || req.workspaceId;
let summary = [];
// returns a mongoose like promise
function addStatusSummary(status) {
let totalItems;
let $regex = `^${status}$`;
let query = {
$and: [{ workspace: workspaceId }, { status: { $regex, $options: 'i' } }],
};
return Report.find(query)
.countDocuments()
.then((numberOfItems) => {
totalItems = numberOfItems;
return Report.find(query)
.sort({ updatedAt: -1 })
.skip(0)
.limit(5);
})
.then((reports) => {
const items = reports.map((r) => r.displayForMember());
summary.push({
status,
items,
totalItems,
});
})
.catch((err) => {
if (!err.statusCode) {
err.statusCode = 500;
}
next(err);
});
}
Workspace.findById(workspaceId)
.then((workspace) => {
let promises = workspace.custom.statusList.map((status) =>
addStatusSummary(status)
);
return Promise.all(promises);
})
.then(() => {
res.status(200).json({
summary,
});
})
.catch((err) => {
if (!err.statusCode) {
err.statusCode = 500;
}
next(err);
});
};
I'm trying to create an Update API route using Sequelize that will:
Capture the record before the update (beforeRec)
Perform the update
Capture the updated record (updatedRec)
Return both the beforeRec and updatedRec
I'm having trouble with my promise chain, which is executing the before and after select queries before executing the update. I've tried several different ways of chaining and capturing results, but here's the latest code:
router.put('/:id', (req, res) => {
const pk = req.params.id;
const getBeforeRec = Master.findByPk(pk)
.then(rec => {return rec})
const updateRec = getBeforeRec
.then(
Master.update(
req.body,
{ where: {id: pk} }
)
)
const getUpdatedRec = updateRec
.then(
Master.findByPk(pk)
.then(rec => {return rec})
);
return Promise.all([getBeforeRec, updateRec, getUpdatedRec])
.then( ([beforeRec, updateRes, afterRec]) => {
return res.json({beforeRec, afterRec})
})
.catch(err => {
return res.status(400).json({'error': err});
});
});
Here's a sanitized example of how the results look:
{
"beforeRec": {
"id": 100,
"updated_col_name": false,
},
"afterRec": {
"id": 100,
"updated_col_name": false,
}
}
In the console, I can see that the update is executing last:
Executing (default): SELECT [id], [updated_col_name] FROM [master] WHERE [master].[id] = N'100';
Executing (default): SELECT [id], [updated_col_name] FROM [master] WHERE [master].[id] = N'100';
Executing (default): UPDATE [master] SET [updated_col_name]=1 WHERE [id] = N'106'
What's the best way to make the second select statement wait for the update?
Any help in clarifying how to chain promises while capturing results along the way will be greatly appreciated! Thanks.
After trying a number of ways, it finally works with nesting:
router.put('/:id', (req, res) => {
const pk = req.params.id;
let beforeRec;
Master.findByPk(pk)
.then(rec => { beforeRec = rec; })
.then(() => {
Master.update(
req.body,
{ where: {id: pk} }
)
.then(() => {
Master.findByPk(pk)
.then(rec => { return rec; })
.then((afterRec) => {
return res.json({beforeRec, afterRec})
})
})
})
.catch(err => {
return res.status(400).json({'error': err});
});
});
If I don't nest the second Master.findByPk, then Master.update() ends up executing last. Also, while I can set beforeRec outside of the promise chain, it didn't work for afterRec.
I don't love it, since I'm still confused by promises, but it's returning the desired results. However, with this nesting mess, I'm not sure where the catch() belongs. Will it catch errors within the nested then()s? Only further testing will tell.
You can do that with , previous method of the instance that returned by update query :
Master.update( req.body , { where: {id: pk} }).then(master => {
console.log(master.get()); // <---- Will give you latest values
console.log(master.previous()); // <---- returns the previous values for all values which have changed
})
For More Detail :
http://docs.sequelizejs.com/class/lib/model.js~Model.html#instance-method-previous
https://github.com/sequelize/sequelize/issues/1814
Give this a shot:
router.put('/:id', (req, res) => {
const pk = req.params.id;
let beforeRec, afterRec;
Master.findByPk(pk)
.then(rec => { beforeRec = rec; })
.then(() => {
Master.update(
req.body,
{ where: {id: pk} }
)
})
.then(() => {
Master.findByPk(pk)
.then(rec => { afterRec = rec; })
})
.then(() => {
res.json({beforeRec, afterRec})
})
.catch(errror => {
res.status(400).json({error});
});
});
Resurrecting an old question to help people in the future...
I've been using sequelize v6 with MySQL. I can't speak to other variances but assuming you just want the snapshot of the "previous" values, you can use the following method to create a copy the properties and their values before updating them
// then catch method
router.put('/:id', (req, res) => {
const pk = req.params.id;
let beforeRecord;
const updateRec = Master.findByPk(pk).then(rec => {
// .get() method is synchronous
beforeRecord = rec.get({ plain: true });
// calling .update on the model instance will also
// call .reload on the instance as well.
// Same thing happens when calling .save on the instance
return rec.update(req.body);
});
updateRec.then(rec => {
const afterRec = rec.get({ plain: true });
return res.json({beforeRec, afterRec})
}).catch(err => {
return res.status(400).json({'error': err});
});
});
// Async await method
router.put('/:id', async (req, res) => {
const pk = req.params.id;
try {
/** #type{import('sequelize').Model} */ // rec equals a sequelize model instance
const rec = await Master.findByPk(pk)
// .get() method is synchronous and returns an object (NOT a sequelize model instance)
const beforeRecord = rec.get({ plain: true });
// calling .update on the model instance will also
// call .reload on the instance as well.
// Same thing happens when calling .save on the instance
await rec.update(req.body); // after this call, rec contains the new updated values
const afterRec = rec.get({ plain: true });
return res.json({beforeRec, afterRec})
} catch (err) {
return res.status(400).json({'error': err});
}
});
I have a request:
Character.count({'character.ownerid': msg.author.id}, function (err, count) {
if (err) {
throw err;
}
if (count > 3) {
err.message = 'Too many characters';
//create error explanation and throw it
throw err;
}
})
If any of there errors do happen I need to exit the whole parent function. I can't put a return in this request since it exits only this method. I thought there is a possibility to do a callback like:
Character.count({'character.ownerid': msg.author.id}, function (err, count, stop) {
But how to work with it? It is inside an anonymous function, I don't know where to place its contents. I also tried to use try/catch but I can't throw errors to the outside handler because of Error: Unhandled "error" event. ([object Object]), see code below:
Character.count({'character.ownerid': msg.author.id}, function (err, count) {
if (err) {
throw err;
}
if (count > 3) {
var err = {};
err.message = 'Too many characters';
throw err;
}
}).then((count) => {
console.log('all good we may continue');
console.log(count);
}).catch((err) => {
if (err != undefined && err.length > 0) {
msg.reply(err.message);
return 0; //exit parent function?
}
});
But even if this worked I am not sure if this code will do what I need. The request is asynchronous so what if the rest of the code gets triggered before then? Is that even possible?
So I basically need to return 0; the parent function somehow and if there are any errors I need to have a handler for them. Any ideas for this?
You seem to be missing the concept. Firstly as already stated, all mongoose operations return a Promise or at least a "Promise like" object which can immediately be resolved via a then() instead of passing in a callback function. This can either present in two ways.
Either with async/await syntax and a try..catch block:
const { Schema } = mongoose = require('mongoose');
const uri = 'mongodb://localhost/test';
mongoose.set('debug', true);
mongoose.Promise = global.Promise;
const characterSchema = new Schema({
name: String
});
const Character = mongoose.model('Character', characterSchema);
const log = data => console.log(JSON.stringify(data,undefined,2));
const doCount = async () => {
let count = await Character.count();
if (count > 3)
throw new Error("too many charaters");
return count;
};
(async function() {
try {
const conn = await mongoose.connect(uri);
await Promise.all(Object.entries(conn.models).map(([k,m]) => m.remove()))
await Character.insertMany(
["Huey","Duey","Louie"].map(name => ({ name }))
);
let count = await doCount();
log({ count });
await Character.create({ name: 'Donald' });
let newCount = await doCount();
console.log("never get here");
} catch(e) {
console.error(e)
} finally {
mongoose.disconnect();
}
})()
Or with standard then() and catch() syntax:
const { Schema } = mongoose = require('mongoose');
const uri = 'mongodb://localhost/test';
mongoose.set('debug', true);
mongoose.Promise = global.Promise;
const characterSchema = new Schema({
name: String
});
const Character = mongoose.model('Character', characterSchema);
const log = data => console.log(JSON.stringify(data,undefined,2));
function doCount() {
return Character.count()
.then(count => {
if (count > 3)
throw new Error("too many charaters");
return count;
});
};
(function() {
mongoose.connect(uri)
.then(conn => Promise.all(
Object.entries(conn.models).map(([k,m]) => m.remove())
))
.then(() => Character.insertMany(
["Huey","Duey","Louie"].map(name => ({ name }))
))
.then(() => doCount())
.then(count => log({ count }))
.then(() => Character.create({ name: 'Donald' }))
.then(() => doCount())
.then(() => console.log("never get here"))
.catch(e => console.error(e))
.then(() => mongoose.disconnect() );
})()
And the output of both listings is just the same:
Mongoose: characters.remove({}, {})
Mongoose: characters.insertMany([ { _id: 5b0f66ec5580010efc5d0602, name: 'Huey', __v: 0 }, { _id: 5b0f66ec5580010efc5d0603, name: 'Duey', __v: 0 }, { _id: 5b0f66ec5580010efc5d0604, name: 'Louie', __v: 0 } ], {})
Mongoose: characters.count({}, {})
{
"count": 3
}
Mongoose: characters.insertOne({ _id: ObjectId("5b0f66ec5580010efc5d0605"), name: 'Donald', __v: 0 })
Mongoose: characters.count({}, {})
Error: too many charaters
at doCount (/home/projects/characters/index.js:20:11)
at <anonymous>
at process._tickCallback (internal/process/next_tick.js:188:7)
As you can see the function happily returns it's value where the count remains 3 or under, but then throws an exception which stops further execution when the count would be greater than 3 since the "never get here" message never gets logged.
So there is no need for "callbacks" here and you would not use one unless you wrapped it in a Promise can did the same type of error handling anyway.
But if you have an "error" then throw the error. This works fine in a promise chain, but a "callback" which does not return as a Promise is simply not part of that chain and can never be "caught". So simply don't use the callback when you don't need to.
Just for kicks, wrapping a callback with a Promise would be done like:
function doCount() {
return new Promise((resolve,reject) =>
Character.count().exec((err,count) => {
if (count > 3)
reject(new Error("too many charaters"));
resolve(count);
})
);
};
But it's noted not to be necessary considering the native methods return something you can resolve as a Promise anyway.
Let's say I have a set of Promises that are making network requests, of which one will fail:
// http://does-not-exist will throw a TypeError
var arr = [ fetch('index.html'), fetch('http://does-not-exist') ]
Promise.all(arr)
.then(res => console.log('success', res))
.catch(err => console.log('error', err)) // This is executed
Let's say I want to wait until all of these have finished, regardless of if one has failed. There might be a network error for a resource that I can live without, but which if I can get, I want before I proceed. I want to handle network failures gracefully.
Since Promise.all doesn't leave any room for this, what is the recommended pattern for handling this, without using a promises library?
Update, you probably want to use the built-in native Promise.allSettled:
Promise.allSettled([promise]).then(([result]) => {
//reach here regardless
// {status: "fulfilled", value: 33}
});
As a fun fact, this answer below was prior art in adding that method to the language :]
Sure, you just need a reflect:
const reflect = p => p.then(v => ({v, status: "fulfilled" }),
e => ({e, status: "rejected" }));
reflect(promise).then((v) => {
console.log(v.status);
});
Or with ES5:
function reflect(promise){
return promise.then(function(v){ return {v:v, status: "fulfilled" }},
function(e){ return {e:e, status: "rejected" }});
}
reflect(promise).then(function(v){
console.log(v.status);
});
Or in your example:
var arr = [ fetch('index.html'), fetch('http://does-not-exist') ]
Promise.all(arr.map(reflect)).then(function(results){
var success = results.filter(x => x.status === "fulfilled");
});
Similar answer, but more idiomatic for ES6 perhaps:
const a = Promise.resolve(1);
const b = Promise.reject(new Error(2));
const c = Promise.resolve(3);
Promise.all([a, b, c].map(p => p.catch(e => e)))
.then(results => console.log(results)) // 1,Error: 2,3
.catch(e => console.log(e));
const console = { log: msg => div.innerHTML += msg + "<br>"};
<div id="div"></div>
Depending on the type(s) of values returned, errors can often be distinguished easily enough (e.g. use undefined for "don't care", typeof for plain non-object values, result.message, result.toString().startsWith("Error:") etc.)
Benjamin's answer offers a great abstraction for solving this issue, but I was hoping for a less abstracted solution. The explicit way to to resolve this issue is to simply call .catch on the internal promises, and return the error from their callback.
let a = new Promise((res, rej) => res('Resolved!')),
b = new Promise((res, rej) => rej('Rejected!')),
c = a.catch(e => { console.log('"a" failed.'); return e; }),
d = b.catch(e => { console.log('"b" failed.'); return e; });
Promise.all([c, d])
.then(result => console.log('Then', result)) // Then ["Resolved!", "Rejected!"]
.catch(err => console.log('Catch', err));
Promise.all([a.catch(e => e), b.catch(e => e)])
.then(result => console.log('Then', result)) // Then ["Resolved!", "Rejected!"]
.catch(err => console.log('Catch', err));
Taking this one step further, you could write a generic catch handler that looks like this:
const catchHandler = error => ({ payload: error, resolved: false });
then you can do
> Promise.all([a, b].map(promise => promise.catch(catchHandler))
.then(results => console.log(results))
.catch(() => console.log('Promise.all failed'))
< [ 'Resolved!', { payload: Promise, resolved: false } ]
The problem with this is that the caught values will have a different interface than the non-caught values, so to clean this up you might do something like:
const successHandler = result => ({ payload: result, resolved: true });
So now you can do this:
> Promise.all([a, b].map(result => result.then(successHandler).catch(catchHandler))
.then(results => console.log(results.filter(result => result.resolved))
.catch(() => console.log('Promise.all failed'))
< [ 'Resolved!' ]
Then to keep it DRY, you get to Benjamin's answer:
const reflect = promise => promise
.then(successHandler)
.catch(catchHander)
where it now looks like
> Promise.all([a, b].map(result => result.then(successHandler).catch(catchHandler))
.then(results => console.log(results.filter(result => result.resolved))
.catch(() => console.log('Promise.all failed'))
< [ 'Resolved!' ]
The benefits of the second solution are that its abstracted and DRY. The downside is you have more code, and you have to remember to reflect all your promises to make things consistent.
I would characterize my solution as explicit and KISS, but indeed less robust. The interface doesn't guarantee that you know exactly whether the promise succeeded or failed.
For example you might have this:
const a = Promise.resolve(new Error('Not beaking, just bad'));
const b = Promise.reject(new Error('This actually didnt work'));
This won't get caught by a.catch, so
> Promise.all([a, b].map(promise => promise.catch(e => e))
.then(results => console.log(results))
< [ Error, Error ]
There's no way to tell which one was fatal and which was wasn't. If that's important then you're going to want to enforce and interface that tracks whether it was successful or not (which reflect does).
If you just want to handle errors gracefully, then you can just treat errors as undefined values:
> Promise.all([a.catch(() => undefined), b.catch(() => undefined)])
.then((results) => console.log('Known values: ', results.filter(x => typeof x !== 'undefined')))
< [ 'Resolved!' ]
In my case, I don't need to know the error or how it failed--I just care whether I have the value or not. I'll let the function that generates the promise worry about logging the specific error.
const apiMethod = () => fetch()
.catch(error => {
console.log(error.message);
throw error;
});
That way, the rest of the application can ignore its error if it wants, and treat it as an undefined value if it wants.
I want my high level functions to fail safely and not worry about the details on why its dependencies failed, and I also prefer KISS to DRY when I have to make that tradeoff--which is ultimately why I opted to not use reflect.
There is a finished proposal for a function which can accomplish this natively, in vanilla Javascript: Promise.allSettled, which has made it to stage 4, is officialized in ES2020, and is implemented in all modern environments. It is very similar to the reflect function in this other answer. Here's an example, from the proposal page. Before, you would have had to do:
function reflect(promise) {
return promise.then(
(v) => {
return { status: 'fulfilled', value: v };
},
(error) => {
return { status: 'rejected', reason: error };
}
);
}
const promises = [ fetch('index.html'), fetch('https://does-not-exist/') ];
const results = await Promise.all(promises.map(reflect));
const successfulPromises = results.filter(p => p.status === 'fulfilled');
Using Promise.allSettled instead, the above will be equivalent to:
const promises = [ fetch('index.html'), fetch('https://does-not-exist/') ];
const results = await Promise.allSettled(promises);
const successfulPromises = results.filter(p => p.status === 'fulfilled');
Those using modern environments will be able to use this method without any libraries. In those, the following snippet should run without problems:
Promise.allSettled([
Promise.resolve('a'),
Promise.reject('b')
])
.then(console.log);
Output:
[
{
"status": "fulfilled",
"value": "a"
},
{
"status": "rejected",
"reason": "b"
}
]
For older browsers, there is a spec-compliant polyfill here.
I really like Benjamin's answer, and how he basically turns all promises into always-resolving-but-sometimes-with-error-as-a-result ones. :)
Here's my attempt at your request just in case you were looking for alternatives. This method simply treats errors as valid results, and is coded similar to Promise.all otherwise:
Promise.settle = function(promises) {
var results = [];
var done = promises.length;
return new Promise(function(resolve) {
function tryResolve(i, v) {
results[i] = v;
done = done - 1;
if (done == 0)
resolve(results);
}
for (var i=0; i<promises.length; i++)
promises[i].then(tryResolve.bind(null, i), tryResolve.bind(null, i));
if (done == 0)
resolve(results);
});
}
var err;
Promise.all([
promiseOne().catch(function(error) { err = error;}),
promiseTwo().catch(function(error) { err = error;})
]).then(function() {
if (err) {
throw err;
}
});
The Promise.all will swallow any rejected promise and store the error in a variable, so it will return when all of the promises have resolved. Then you can re-throw the error out, or do whatever. In this way, I guess you would get out the last rejection instead of the first one.
I had the same problem and have solved it in the following way:
const fetch = (url) => {
return node-fetch(url)
.then(result => result.json())
.catch((e) => {
return new Promise((resolve) => setTimeout(() => resolve(fetch(url)), timeout));
});
};
tasks = [fetch(url1), fetch(url2) ....];
Promise.all(tasks).then(......)
In that case Promise.all will wait for every Promise will come into resolved or rejected state.
And having this solution we are "stopping catch execution" in a non-blocking way. In fact, we're not stopping anything, we just returning back the Promise in a pending state which returns another Promise when it's resolved after the timeout.
This should be consistent with how Q does it:
if(!Promise.allSettled) {
Promise.allSettled = function (promises) {
return Promise.all(promises.map(p => Promise.resolve(p).then(v => ({
state: 'fulfilled',
value: v,
}), r => ({
state: 'rejected',
reason: r,
}))));
};
}
Instead of rejecting, resolve it with a object.
You could do something like this when you are implementing promise
const promise = arg => {
return new Promise((resolve, reject) => {
setTimeout(() => {
try{
if(arg != 2)
return resolve({success: true, data: arg});
else
throw new Error(arg)
}catch(e){
return resolve({success: false, error: e, data: arg})
}
}, 1000);
})
}
Promise.all([1,2,3,4,5].map(e => promise(e))).then(d => console.log(d))
Benjamin Gruenbaum answer is of course great,. But I can also see were Nathan Hagen point of view with the level of abstraction seem vague. Having short object properties like e & v don't help either, but of course that could be changed.
In Javascript there is standard Error object, called Error,. Ideally you always throw an instance / descendant of this. The advantage is that you can do instanceof Error, and you know something is an error.
So using this idea, here is my take on the problem.
Basically catch the error, if the error is not of type Error, wrap the error inside an Error object. The resulting array will have either resolved values, or Error objects you can check on.
The instanceof inside the catch, is in case you use some external library that maybe did reject("error"), instead of reject(new Error("error")).
Of course you could have promises were you resolve an error, but in that case it would most likely make sense to treat as an error anyway, like the last example shows.
Another advantage of doing it this, array destructing is kept simple.
const [value1, value2] = PromiseAllCatch(promises);
if (!(value1 instanceof Error)) console.log(value1);
Instead of
const [{v: value1, e: error1}, {v: value2, e: error2}] = Promise.all(reflect..
if (!error1) { console.log(value1); }
You could argue that the !error1 check is simpler than an instanceof, but your also having to destruct both v & e.
function PromiseAllCatch(promises) {
return Promise.all(promises.map(async m => {
try {
return await m;
} catch(e) {
if (e instanceof Error) return e;
return new Error(e);
}
}));
}
async function test() {
const ret = await PromiseAllCatch([
(async () => "this is fine")(),
(async () => {throw new Error("oops")})(),
(async () => "this is ok")(),
(async () => {throw "Still an error";})(),
(async () => new Error("resolved Error"))(),
]);
console.log(ret);
console.log(ret.map(r =>
r instanceof Error ? "error" : "ok"
).join(" : "));
}
test();
I think the following offers a slightly different approach... compare fn_fast_fail() with fn_slow_fail()... though the latter doesn't fail as such... you can check if one or both of a and b is an instance of Error and throw that Error if you want it to reach the catch block (e.g. if (b instanceof Error) { throw b; }) . See the jsfiddle.
var p1 = new Promise((resolve, reject) => {
setTimeout(() => resolve('p1_delayed_resolvement'), 2000);
});
var p2 = new Promise((resolve, reject) => {
reject(new Error('p2_immediate_rejection'));
});
var fn_fast_fail = async function () {
try {
var [a, b] = await Promise.all([p1, p2]);
console.log(a); // "p1_delayed_resolvement"
console.log(b); // "Error: p2_immediate_rejection"
} catch (err) {
console.log('ERROR:', err);
}
}
var fn_slow_fail = async function () {
try {
var [a, b] = await Promise.all([
p1.catch(error => { return error }),
p2.catch(error => { return error })
]);
console.log(a); // "p1_delayed_resolvement"
console.log(b); // "Error: p2_immediate_rejection"
} catch (err) {
// we don't reach here unless you throw the error from the `try` block
console.log('ERROR:', err);
}
}
fn_fast_fail(); // fails immediately
fn_slow_fail(); // waits for delayed promise to resolve
I just wanted a polyfill that exactly replicated ES2020 behaviour since I'm locked into node versions a lot earlier than 12.9 (when Promise.allSettled appeared), unfortunately. So for what it's worth, this is my version:
const settle = (promise) => (promise instanceof Promise) ?
promise.then(val => ({ value: val, status: "fulfilled" }),
err => ({ reason: err, status: "rejected" })) :
{ value: promise, status: 'fulfilled' };
const allSettled = async (parr) => Promise.all(parr.map(settle));
This handles a mixed array of promise and non-promise values, as does the ES version. It hands back the same array of { status, value/reason } objects as the native version.
Here's my custom settledPromiseAll()
const settledPromiseAll = function(promisesArray) {
var savedError;
const saveFirstError = function(error) {
if (!savedError) savedError = error;
};
const handleErrors = function(value) {
return Promise.resolve(value).catch(saveFirstError);
};
const allSettled = Promise.all(promisesArray.map(handleErrors));
return allSettled.then(function(resolvedPromises) {
if (savedError) throw savedError;
return resolvedPromises;
});
};
Compared to Promise.all
If all promises are resolved, it performs exactly as the standard one.
If one of more promises are rejected, it returns the first one rejected much the same as the standard one but unlike it waits for all promises to resolve/reject.
For the brave we could change Promise.all():
(function() {
var stdAll = Promise.all;
Promise.all = function(values, wait) {
if(!wait)
return stdAll.call(Promise, values);
return settledPromiseAll(values);
}
})();
CAREFUL. In general we never change built-ins, as it might break other unrelated JS libraries or clash with future changes to JS standards.
My settledPromiseall is backward compatible with Promise.all and extends its functionality.
People who are developing standards -- why not include this to a new Promise standard?
I recently built a library that allows what you need. it executes promises in parallel, and if one fails, the process continues, at the end it returns an array with all the results, including errors.
https://www.npmjs.com/package/promise-ax
I hope and it is helpful for someone.
const { createPromise } = require('promise-ax');
const promiseAx = createPromise();
const promise1 = Promise.resolve(4);
const promise2 = new Promise((resolve, reject) => setTimeout(reject, 100, new Error("error")));
const promise3 = Promise.reject("error");
const promise4 = promiseAx.resolve(8);
const promise5 = promiseAx.reject("errorAx");
const asyncOperation = (time) => {
return new Promise((resolve, reject) => {
if (time < 0) {
reject("reject");
}
setTimeout(() => {
resolve(time);
}, time);
});
};
const promisesToMake = [promise1, promise2, promise3, promise4, promise5, asyncOperation(100)];
promiseAx.allSettled(promisesToMake).then((results) => results.forEach((result) => console.log(result)));
// Salida esperada:
// 4
// Error: error
// error
// 8
// errorAx
// 100
I would do:
var err = [fetch('index.html').then((success) => { return Promise.resolve(success); }).catch((e) => { return Promise.resolve(e); }),
fetch('http://does-not-exist').then((success) => { return Promise.resolve(success); }).catch((e) => { return Promise.resolve(e); })];
Promise.all(err)
.then(function (res) { console.log('success', res) })
.catch(function (err) { console.log('error', err) }) //never executed
I've been using following codes since ES5.
Promise.wait = function(promiseQueue){
if( !Array.isArray(promiseQueue) ){
return Promise.reject('Given parameter is not an array!');
}
if( promiseQueue.length === 0 ){
return Promise.resolve([]);
}
return new Promise((resolve, reject) =>{
let _pQueue=[], _rQueue=[], _readyCount=false;
promiseQueue.forEach((_promise, idx) =>{
// Create a status info object
_rQueue.push({rejected:false, seq:idx, result:null});
_pQueue.push(Promise.resolve(_promise));
});
_pQueue.forEach((_promise, idx)=>{
let item = _rQueue[idx];
_promise.then(
(result)=>{
item.resolved = true;
item.result = result;
},
(error)=>{
item.resolved = false;
item.result = error;
}
).then(()=>{
_readyCount++;
if ( _rQueue.length === _readyCount ) {
let result = true;
_rQueue.forEach((item)=>{result=result&&item.resolved;});
(result?resolve:reject)(_rQueue);
}
});
});
});
};
The usage signature is just like Promise.all. The major difference is that Promise.wait will wait for all the promises to finish their jobs.
I know that this question has a lot of answers, and I'm sure must (if not all) are correct.
However it was very hard for me to understand the logic/flow of these answers.
So I looked at the Original Implementation on Promise.all(), and I tried to imitate that logic - with the exception of not stopping the execution if one Promise failed.
public promiseExecuteAll(promisesList: Promise<any>[] = []): Promise<{ data: any, isSuccess: boolean }[]>
{
let promise: Promise<{ data: any, isSuccess: boolean }[]>;
if (promisesList.length)
{
const result: { data: any, isSuccess: boolean }[] = [];
let count: number = 0;
promise = new Promise<{ data: any, isSuccess: boolean }[]>((resolve, reject) =>
{
promisesList.forEach((currentPromise: Promise<any>, index: number) =>
{
currentPromise.then(
(data) => // Success
{
result[index] = { data, isSuccess: true };
if (promisesList.length <= ++count) { resolve(result); }
},
(data) => // Error
{
result[index] = { data, isSuccess: false };
if (promisesList.length <= ++count) { resolve(result); }
});
});
});
}
else
{
promise = Promise.resolve([]);
}
return promise;
}
Explanation:
- Loop over the input promisesList and execute each Promise.
- No matter if the Promise resolved or rejected: save the Promise's result in a result array according to the index. Save also the resolve/reject status (isSuccess).
- Once all Promises completed, return one Promise with the result of all others.
Example of use:
const p1 = Promise.resolve("OK");
const p2 = Promise.reject(new Error(":-("));
const p3 = Promise.resolve(1000);
promiseExecuteAll([p1, p2, p3]).then((data) => {
data.forEach(value => console.log(`${ value.isSuccess ? 'Resolve' : 'Reject' } >> ${ value.data }`));
});
/* Output:
Resolve >> OK
Reject >> :-(
Resolve >> 1000
*/
You can execute your logic sequentially via synchronous executor nsynjs. It will pause on each promise, wait for resolution/rejection, and either assign resolve's result to data property, or throw an exception (for handling that you will need try/catch block). Here is an example:
function synchronousCode() {
function myFetch(url) {
try {
return window.fetch(url).data;
}
catch (e) {
return {status: 'failed:'+e};
};
};
var arr=[
myFetch("https://ajax.googleapis.com/ajax/libs/jquery/2.0.0/jquery.min.js"),
myFetch("https://ajax.googleapis.com/ajax/libs/jquery/2.0.0/NONEXISTANT.js"),
myFetch("https://ajax.NONEXISTANT123.com/ajax/libs/jquery/2.0.0/NONEXISTANT.js")
];
console.log('array is ready:',arr[0].status,arr[1].status,arr[2].status);
};
nsynjs.run(synchronousCode,{},function(){
console.log('done');
});
<script src="https://rawgit.com/amaksr/nsynjs/master/nsynjs.js"></script>
Promise.all with using modern async/await approach
const promise1 = //...
const promise2 = //...
const data = await Promise.all([promise1, promise2])
const dataFromPromise1 = data[0]
const dataFromPromise2 = data[1]
I don't know which promise library you are using, but most have something like allSettled.
Edit: Ok since you want to use plain ES6 without external libraries, there is no such method.
In other words: You have to loop over your promises manually and resolve a new combined promise as soon as all promises are settled.