Issue resolving all promises - javascript

I have a code block where I want to concatenate results of two database queries. So I tried implementing Promises.all
const promise_list = []
let appData = [];
let promise = new Promise((resolve, reject) => {
let database = new Database();
database.query(`select * from configuration where version = (select max(version) from configuration) OR version= ? ORDER BY version `, [req.body.app_version])
.then(rows => {
appData=rows[0];
database.close()
resolve()
}, err => {
return database.close().then(() => { throw err; })
})
.catch(err => {
console.log(err);
res.status(500).json("Database Error");
reject()
})
});
promise_list.push(promise)
let promise2 = new Promise((resolve, reject) => {
let database = new Database();
database.query(`select points from users where id=?`, [req.user.id])
.then(rows => {
appData.points=rows[0]['points'];
database.close()
resolve()
}, err => {
return database.close().then(() => { throw err; })
})
.catch(err => {
console.log(err);
res.status(500).json("Database Error");
reject()
})
});
promise_list.push(promise2)
Promise.all(promise_list).then(result => {
res.status(200).json(appData);
});
The second query works sometimes and sometimes it doesnt. What could be the issue?

appData.points=rows[0]['points']; only works if appData has been initialized by the other promise first. But with Promise.all, either promise can be resolved first. If the first promise resolve second, it will simply override whatever value appData currently has.
It looks like you are using promises incorrectly. Instead of using them with side-effects (assigning to appData), you should resolve them properly.
The whole code can be cleaned up a lot to something like this:
let database = new Database();
let promise = database.query(`select * from configuration where version = (select max(version) from configuration) OR version= ? ORDER BY version `, [req.body.app_version])
.then(rows => rows[0]);
let promise2 = database.query(`select points from users where id=?`, [req.user.id])
.then(rows => rows[0].points);
Promise.all([promise, promise2])
.then(
([appData, points]) => {
appData.points = points;
res.status(200).json(appData);
},
err => {
console.log(err);
database.close().then(() => {
res.status(500).json("Database Error");
});
}
);
Don't know what Database does, so it's not clear whether it's OK to only call it once. But it should you a better idea for how to use promises.

Related

Promise data and exception handling

I am confused with the use of promise, specifically of its way of data manipulation (passing values from block to block) and exception handling (bubbling up the error). I am trying to learn a right way to use promise and to handle error, something like
Error: A caught error.
at promiseTwo()
at promiseOne()
at subprocess()
at mainprocess()
Here are my two attempts in implementing them:
Attempt 1: Clumsy, deeply nested, and errors are uncaught.
var subprocess = () => {
return new Promise((resolve, reject) => {
promiseOne().then(data1 => {
// Some code with data1, throw some error
promiseTwo().then(data2 => {
// Some code with data1n2, throw some error
promiseThree().then(data3 => {
// Data manipulation with data1, data2, and data3
return resolve(<...>)
}).catch(err3 => { throw err3 })
}.catch(err2n3 => { throw err2n3 }) // >>> ERR: Cannot get err3.
}.catch(err1n2n3 => { return reject(err1n2n3) }) // >>> ERR: Cannot get err3 or err2.
}
}
return new Promise((resolve, reject) => {
subprocess().then(data => {
// TODO
}).catch(allErr => { return reject(allErr) }
}
Attempt 2: Unable to use data from previous promise block.
var subprocess = () => {
return new Promise((resolve, reject) => {
promiseOne()
.then(data1 => {
// Some code with data1, throw some error
return promiseTwo()
})
.then(data2 => {
// Some code with data1n2, throw some error
// >>> ERR: Cannot get data1
return promiseThree()
})
.then(data3 => {
// Data manipulation with data1, data2, and data3
// >>> ERR: Cannot get data1 and data2
return resolve(<...>)
})
.catch(err1n2n3 => {
return reject(err1n2n3)
})
}
}
return new Promise((resolve, reject) => {
subprocess().then(data => {
// Some code, throw some error
}).catch(allErr => { return reject(allErr) }
}
Note: Some of the promise block (i.e. promiseOne, promiseTwo, etc.) are pre-defined so I do not have control over what data they will return. I am sure there are more errors in the attempts (e.g. if returning a function is a right way to do it).
Please help. Thanks.
for this kind of situation, you can combine promises and async-await together.
From the question, it seems we have three promises and one function that executes and handle them.
You can try something like this -
const subProcess = () => {
return new Promise((resolve, reject) => {
// Using IIFE ( You shouldn't put async keyword on promise callbac )
(async () => {
// Use of try catch to handle the errors
try {
await promiseOne()
await promiseTwo()
await promiseThree()
// Additional code if need after them
} catch(err){
// Handle error ( all three promise error will be transferred here )
}
})()
})
}
The above code waits for the promises to execute one by one and also catch error from all three promises if any.
And as #samuei mentioned, you can also use Promise.all() in this.
const subProcess = () => {
return new Promise((resolve, reject) => {
// Using IIFE ( You shouldn't put async keyword on promise callbac )
(async () => {
// Use of try catch to handle the errors
try {
const myPromises = [promiseOne, promiseTwo, promiseThree];
const res = await Promise.all(myPromises);
// Additional code if need after them
} catch(err){
// Handle error ( all three promise error will be transferred here )
}
})()
})
}
And if you don't want to use async-await then you can do something like this as well
const subProcess = () => {
return new Promise((resolve, reject) => {
const myPromises = [];
const myPromises = [promiseOne, promiseTwo, promiseThree];
Promise.all(myPromises)
.then(res => {
// Handle the response
})
.catch(err => {
// Handle the error
})
})
}
It sounds like you're looking for Promise.all, which lets you set a series of promises in motion, then deal with the results when they are all resolved.

Using Promises with readdir to diplay directory

I know that there are better ways to do this and tools such as serve-index already exist as an npm package to list a directory structure, but I wanted to test this out on my as I am still rather new to using Node.js and JavaScript.
Currently, my code is as follows, and I am confused as to why my array is empty.
let directoryTree = [];
let directoryList = new Promise((resolve, reject) => {
resolve(fs.readdir(path.join(__dirname, '..'), (err, fileList) => {
for (file of fileList) {
console.log(file);
directoryTree.push(file);
}
}));
}).then(() => console.log(directoryTree))
.catch((err) => console.log(error));
What gets displayed is
[]
app
files
node_modules
package-lock.json
package.json
This indicates to me that the promise is being resolved before readdir finishes executing, otherwise the empty array wouldn't be logged first. I believe that something may be incorrect in my usage of promises then, but I might be wrong. I appreciate any advice!
You can call resolve() to resolve the promise when the callback is done fetching the data asynchronously.
Then when the data is fetched and resolved you would get the list of files in the then() callback:
new Promise((resolve, reject) => {
fs.readdir(path.join(__dirname, '..'), (err, fileList) => {
err ? reject(err) : resolve(fileList); //resolve with list of files
});
})
.then((list) => console.log(list))
.catch((err) => console.log(err));
You're using Promise incorrectly.
The syntax is (from MDN):
const myFirstPromise = new Promise((resolve, reject) => {
// do something asynchronous which eventually calls either:
//
resolve(someValue); // fulfilled
// or
reject("failure reason"); // rejected
});
Refactoring:
let directoryList = new Promise((resolve, reject) => {
fs.readdir(path.join(__dirname, '..'), (err, fileList) => {
if (err) reject(err)
else resolve(fileList)
});
})
// directoryList is a Promise that could reject or resolve
directoryList.then(() => console.log(directoryTree))
.catch((err) => console.log(error));

javascript promise after foreach loop with multiple mongoose find

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);
});
};

Each then() should return a value or throw when using Promises

I have a few async methods that I need to wait for completion before I return from the request. I'm using Promises, but I keep getting the error:
Each then() should return a value or throw // promise/always-return
Why is this happpening? This is my code:
router.get('/account', function(req, res) {
var id = req.user.uid
var myProfile = {}
var profilePromise = new Promise(function(resolve, reject) {
var userRef = firebase.db.collection('users').doc(id)
userRef.get()
.then(doc => { // Error occurs on this line
if (doc.exists) {
var profile = doc.data()
profile.id = doc.id
myProfile = profile
resolve()
} else {
reject(Error("Profile doesn't exist"))
}
})
.catch(error => {
reject(error)
})
})
// More promises further on, which I wait for
})
Add at the end of the then()
return null
That's it.
Each then() should return a value or throw Firebase cloud functions
Just avoid the Promise constructor antipattern! If you don't call resolve but return a value, you will have something to return. The then method should be used for chaining, not just subscribing:
outer.get('/account', function(req, res) {
var id = req.user.uid
var userRef = firebase.db.collection('users').doc(id)
var profilePromise = userRef.get().then(doc => {
if (doc.exists) {
var profile = doc.data()
profile.id = doc.id
return profile // I assume you don't want to return undefined
// ^^^^^^
} else {
throw new Error("Profile doesn't exist")
// ^^^^^
}
})
// More promises further on, which I wait for:
// profilePromise.then(myProfile => { … });
})
In your case firebase.db.collection('users').doc(id) returning promise itself, please check firebase snippet to here for node-js.
If you have multiple promises and you need to call them one by one then use Promises chaining.
Please check this article this will help you.
Use following code in your case,
router.get('/account', function(req, res) {
var id = req.user.uid;
var myProfile = {};
var userRef = firebase.db.collection('users').doc(id)
userRef.get()
.then(doc => {
if (!doc || !doc.exists) {
throw new Error("Profile doesn't exist")
}
var profile = doc.data();
profile.id = doc.id;
myProfile = profile;
return myProfile;
})
.catch(error => {
console.log('error', error);
})
})
And use Promise.all if you have multiple promises and you want's to execute them in once.
The Promise.all(iterable) method returns a single Promise that resolves when all of the promises in the iterable argument have resolved or when the iterable argument contains no promises. It rejects with the reason of the first promise that rejects.
For example:
var promise1 = new Promise((resolve, reject) => {
setTimeout(resolve, 100, 'foo1');
});
var promise2 = new Promise((resolve, reject) => {
setTimeout(resolve, 100, 'foo2');
});
var promise3 = new Promise((resolve, reject) => {
setTimeout(resolve, 100, 'foo3');
});
Promise.all([promise1, promise2, promise3])
.then(result => console.log(result))
//result [foo1, foo2, foo3]
Hopes this will help you !!
If you can't fix this issue but still want to run your code...
open : eslintrc.json file (search from project root directory)
search : 'promise/always-return'
change : Case 1: if (existing value is 2) => change to 1
Case 2: else if(existing value is "error" => change to "warn")
It will make this error into warning, but be careful with it... Also use eslint plungin in your editor to remind of good practice. Otherwise you won't get any promise/always-return related warnings.
Also make sure you find the right eslintrc.json if more than 1 appears on your search
why is this happening ? Each then() should return a value or throw So I am adding this since the why was never explained.
For anyone else wondering the why. this is not a normal JS Error. This is a direct result of using the ES-Lint promise package:
https://github.com/xjamundx/eslint-plugin-promise
https://github.com/xjamundx/eslint-plugin-promise/blob/development/rules/always-return.js
This package has a ruleset to error when it does not find a return from a promise. This can be helpful to identify promise flow errors. You could fix this by simply adding a return as thats what the linter is triggered by or editing the es-lint rule set (not recommended). Thats why I assume it works when it is not being linted causing your confusion.
router.get('/account', function(req, res) {
var id = req.user.uid
var myProfile = {}
var profilePromise = new Promise(function(resolve, reject) {
var userRef = firebase.db.collection('users').doc(id)
userRef.get()
.then(doc => { // Error occurs on this line
if (doc.exists) {
var profile = doc.data()
profile.id = doc.id
myProfile = profile
return null
} else {
reject(Error("Profile doesn't exist"))
}
})
.catch(error => {
reject(error)
})
})
// More promises further on, which I wait for
})
Here is a list of the default rulesets used by that package. Hope it helps anyone else trying to get background on why this return was needed.
{
"rules": {
"promise/always-return": "error",
"promise/no-return-wrap": "error",
"promise/param-names": "error",
"promise/catch-or-return": "error",
"promise/no-native": "off",
"promise/no-nesting": "warn",
"promise/no-promise-in-callback": "warn",
"promise/no-callback-in-promise": "warn",
"promise/avoid-new": "warn",
"promise/no-new-statics": "error",
"promise/no-return-in-finally": "warn",
"promise/valid-params": "warn"
}

Is it safe to resolve a promise multiple times?

I have an i18n service in my application which contains the following code:
var i18nService = function() {
this.ensureLocaleIsLoaded = function() {
if( !this.existingPromise ) {
this.existingPromise = $q.defer();
var deferred = this.existingPromise;
var userLanguage = $( "body" ).data( "language" );
this.userLanguage = userLanguage;
console.log( "Loading locale '" + userLanguage + "' from server..." );
$http( { method:"get", url:"/i18n/" + userLanguage, cache:true } ).success( function( translations ) {
$rootScope.i18n = translations;
deferred.resolve( $rootScope.i18n );
} );
}
if( $rootScope.i18n ) {
this.existingPromise.resolve( $rootScope.i18n );
}
return this.existingPromise.promise;
};
The idea is that the user would call ensureLocaleIsLoaded and wait for the promise to be resolved. But given that the purpose of the function is to only ensure that the locale is loaded, it would be perfectly fine for the user to invoke it several times.
I'm currently just storing a single promise and resolve it if the user calls the function again after the locale has been successfully retrieved from the server.
From what I can tell, this is working as intended, but I'm wondering if this is a proper approach.
As I understand promises at present, this should be 100% fine. The only thing to understand is that once resolved (or rejected), that is it for a defered object - it is done.
If you call then(...) on its promise again, you immediately get the (first) resolved/rejected result.
Additional calls to resolve() will not have any effect.
Below is an executable snippet that covers those use cases:
var p = new Promise((resolve, reject) => {
resolve(1);
reject(2);
resolve(3);
});
p.then(x => console.log('resolved to ' + x))
.catch(x => console.log('never called ' + x));
p.then(x => console.log('one more ' + x));
p.then(x => console.log('two more ' + x));
p.then(x => console.log('three more ' + x));
I faced the same thing a while ago, indeed a promise can be only resolved once, another tries will do nothing (no error, no warning, no then invocation).
I decided to work it around like this:
getUsers(users => showThem(users));
getUsers(callback){
callback(getCachedUsers())
api.getUsers().then(users => callback(users))
}
just pass your function as a callback and invoke it as many times you wish! Hope that makes sense.
There s no clear way to resolve promises multiple times because since it's resolved it's done. The better approach here is to use observer-observable pattern for example i wrote following code that observes socket client event. You can extend this code to met your need
const evokeObjectMethodWithArgs = (methodName, args) => (src) => src[methodName].apply(null, args);
const hasMethodName = (name) => (target = {}) => typeof target[name] === 'function';
const Observable = function (fn) {
const subscribers = [];
this.subscribe = subscribers.push.bind(subscribers);
const observer = {
next: (...args) => subscribers.filter(hasMethodName('next')).forEach(evokeObjectMethodWithArgs('next', args))
};
setTimeout(() => {
try {
fn(observer);
} catch (e) {
subscribers.filter(hasMethodName('error')).forEach(evokeObjectMethodWithArgs('error', e));
}
});
};
const fromEvent = (target, eventName) => new Observable((obs) => target.on(eventName, obs.next));
fromEvent(client, 'document:save').subscribe({
async next(document, docName) {
await writeFilePromise(resolve(dataDir, `${docName}`), document);
client.emit('document:save', document);
}
});
If you need to change the return value of promise, simply return new value in then and chain next then/catch on it
var p1 = new Promise((resolve, reject) => { resolve(1) });
var p2 = p1.then(v => {
console.log("First then, value is", v);
return 2;
});
p2.then(v => {
console.log("Second then, value is", v);
});
You can write tests to confirm the behavior.
By running the following test you can conclude that
The resolve()/reject() call never throw error.
Once settled (rejected), the resolved value (rejected error) will be preserved
regardless of following resolve() or reject() calls.
You can also check my blog post for details.
/* eslint-disable prefer-promise-reject-errors */
const flipPromise = require('flip-promise').default
describe('promise', () => {
test('error catch with resolve', () => new Promise(async (rs, rj) => {
const getPromise = () => new Promise(resolve => {
try {
resolve()
} catch (err) {
rj('error caught in unexpected location')
}
})
try {
await getPromise()
throw new Error('error thrown out side')
} catch (e) {
rs('error caught in expected location')
}
}))
test('error catch with reject', () => new Promise(async (rs, rj) => {
const getPromise = () => new Promise((_resolve, reject) => {
try {
reject()
} catch (err) {
rj('error caught in unexpected location')
}
})
try {
await getPromise()
} catch (e) {
try {
throw new Error('error thrown out side')
} catch (e){
rs('error caught in expected location')
}
}
}))
test('await multiple times resolved promise', async () => {
const pr = Promise.resolve(1)
expect(await pr).toBe(1)
expect(await pr).toBe(1)
})
test('await multiple times rejected promise', async () => {
const pr = Promise.reject(1)
expect(await flipPromise(pr)).toBe(1)
expect(await flipPromise(pr)).toBe(1)
})
test('resolve multiple times', async () => {
const pr = new Promise(resolve => {
resolve(1)
resolve(2)
resolve(3)
})
expect(await pr).toBe(1)
})
test('resolve then reject', async () => {
const pr = new Promise((resolve, reject) => {
resolve(1)
resolve(2)
resolve(3)
reject(4)
})
expect(await pr).toBe(1)
})
test('reject multiple times', async () => {
const pr = new Promise((_resolve, reject) => {
reject(1)
reject(2)
reject(3)
})
expect(await flipPromise(pr)).toBe(1)
})
test('reject then resolve', async () => {
const pr = new Promise((resolve, reject) => {
reject(1)
reject(2)
reject(3)
resolve(4)
})
expect(await flipPromise(pr)).toBe(1)
})
test('constructor is not async', async () => {
let val
let val1
const pr = new Promise(resolve => {
val = 1
setTimeout(() => {
resolve()
val1 = 2
})
})
expect(val).toBe(1)
expect(val1).toBeUndefined()
await pr
expect(val).toBe(1)
expect(val1).toBe(2)
})
})
What you should do is put an ng-if on your main ng-outlet and show a loading spinner instead. Once your locale is loaded the you show the outlet and let the component hierarchy render. This way all of your application can assume that the locale is loaded and no checks are necessary.
No. It is not safe to resolve/reject promise multiple times. It is basically a bug, that is hard to catch, becasue it can be not always reproducible.
There is pattern that can be used to trace such issues in debug time. Great lecture on this topic: Ruben Bridgewater — Error handling: doing it right! (the part related to the question is around 40 min)
see github gist: reuse_promise.js
/*
reuse a promise for multiple resolve()s since promises only resolve once and then never again
*/
import React, { useEffect, useState } from 'react'
export default () => {
const [somePromise, setSomePromise] = useState(promiseCreator())
useEffect(() => {
somePromise.then(data => {
// do things here
setSomePromise(promiseCreator())
})
}, [somePromise])
}
const promiseCreator = () => {
return new Promise((resolve, reject) => {
// do things
resolve(/*data*/)
})
}

Categories