So, I have a piece of code that makes use of Promises and I am using the bluebird library.
The problem is that my promise gets rejected even if there is no error. It completely skips the .then block even for a simple console.log
Here is the code that makes use of promise:
function returnMeetings(query) {
return new Promise((reject, resolve) => {
Meeting.find(query, (err, foundMeetings) => {
if (err) {
console.log("We have a error")
reject(err);
}
resolve(foundMeetings);
})
})
}
And here is the code that makes use of that returnMeetings function
returnMeetings(query)
.then((data) => {
console.log("here we are")
// while (count < stopAt) {
// let localData = [];
// if (req.params.duration === 'monthly') {
// let {
// date1,
// date2
// } = twoDates(count, count);
// localData = data.filter((el) => {
// if (el.startDate) {
// let localDate = new Date(el.startDate);
// if (localDate >= date1 && localDate <= date2) {
// return el;
// }
// }
// })
// if (localData) {
// data.push({
// data: localData,
// month: count
// })
// }
//
// if (count === stopAt - 1) {
// myEmitter.emit('found all meetings')
// } else {
// count++;
// }
// }
// }
}).catch((err) => {
res.status(500).json({
message: err
})
})
As you can see in the returnMeetings function i have placed a console.log inside the if(err) block, and it never runs, from that I conclude that I didn't receive any error from the mongoose query.
Still, the code completely skips the .then block, and falls into the catch chain. I have a simple console.log inside the then block, and an interesting thing to note is that the value of err in the callback inside catch block is an array of mongodb documents.
Can someone explain, why is my code behaving in this manner?
Thanks for your help.
the order of resolve/reject in your Promise constructor callback is wrong ... the names of the functions are irrelevant, the first is to resolve, the second is to reject ... you are actually rejecting the promise when you call resolve
i.e. you could
return new Promise((fred, wilma) => {
then to resolve the promise, you would call fred(value) and to reject you would call wilma(error)
in other words, the names you give the callback arguments is irrelevant, the position is relevant
change your code as follows:
function returnMeetings(query) {
return new Promise((resolve, reject) => {
Meeting.find(query, (err, foundMeetings) => {
if (err) {
console.log("We have a error")
reject(err);
}
resolve(foundMeetings);
})
})
}
Related
I have not worked with Javascript in a long time, so now promises are a new concept to me. I have some operations requiring more than one asynchronous call but which I want to treat as a transaction where steps do not execute if the step before failed. Currently I chain promises by nesting and I want to return a promise to the caller.
After reading the chaining section of Mozilla's Using Promises guide, I'm not sure if what I'm doing is correct or equivalent to the "callback pyramid of doom".
Is there a cleaner way to do this (besides chaining with a guard check in each then)? Am I right in my belief that in Mozilla's example it will execute each chained then even when there is an error?
myfunction(key) => {
return new Promise((outerResolve, outerReject) => {
return new Promise((resolve, reject) => {
let item = cache.get(key);
if (item) {
resolve(item);
} else {
//we didnt have the row cached, load it from store
chrome.storage.sync.get(key, function (result) {
chrome.runtime.lastError
? reject({ error: chrome.runtime.lastError.message })
: resolve(result);
});
}
}).then((resolve) => {
//Now the inner most item is resolved, we are working in the 'outer' shell
if (resolve.error) {
outerReject(resolve);
} else {
//No error, continue
new Promise((resolve, reject) => {
chrome.storage.sync.get(keyBasedOnPreviousData, function (result) {
chrome.runtime.lastError
? reject({ error: chrome.runtime.lastError.message })
: resolve(result);
});
}).then((resolve) => {
//finally return the result to the caller
if (resolve.error) {
outerReject(resolve);
} else {
outerResolve(resolve);
}
});
}
});
});
}
Subsequent then statements are not executed (until a catch) when an exception is thrown. Also, .then returns a Promise, so you don't need to create an additional, outer Promise.
Try this example:
var p = new Promise((resolve, reject) => {
console.log('first promise, resolves');
resolve();
})
.then(() => {
throw new Error('Something failed');
})
.then(() => {
console.log('then after the error');
return('result');
});
p.then(res => console.log('success: ' + res), err => console.log('error: ' + err));
You will not see "then after the error" in the console, because that happens after an exception is thrown. But if you comment the throw statement, you will get the result you expect in the Promise.
I am not sure I understand your example entirely, but I think it could be simplified like this:
myfunction(key) => {
return new Promise((resolve, reject) => {
let item = cache.get(key);
if (item) {
resolve(item);
} else {
//we didnt have the row cached, load it from store
chrome.storage.sync.get(key, function (result) {
chrome.runtime.lastError
? throw new Error(chrome.runtime.lastError.message)
: resolve(result);
});
}
}).then((previousData) => {
// keyBasedOnPreviousData is calculated based on previousData
chrome.storage.sync.get(keyBasedOnPreviousData, function (result) {
chrome.runtime.lastError
? throw new Error(chrome.runtime.lastError.message)
: return result;
});
});
}
It's a bit of a mess. This is my attempt at rewriting. A good thing to try to avoid is new Promise().
function chromeStorageGet(key) {
return new Promise( (res, rej) => {
chrome.storage.sync.get(key, result => {
if (chrome.runtime.lastError) {
rej(new Error(chrome.runtime.lastError.message))
} else {
res(result)
}
});
});
});
function myfunction(key) {
const item = cache.get(key) ? Promise.resolve(cache.get(key)) : chromeStorageGet(key);
return item.then( cacheResult => {
return chromeStorageGet(keyBasedOnPreviousData);
});
}
Why avoid new Promise()?
The reason for this is that you want to do every step with then(). If any error happened in any of the promises, every promise in the chain will fail and any subsequent then() will not get executed until there is a catch() handler.
Lots of promise based-code requires no error handlers, because promise-based functions always return promises and exceptions should flow all the back to the caller until there is something useful to be done with error handling.
Note that the exceptions to these 2 rules are in my chromeStorageGet function. A few notes here:
new Promise can be a quick and easy way to convert callback code to promise code.
It's usually a good idea to just create a little conversion layer for this callback-based code. If you need chrome.storage.sync in other places, maybe create a little utility that promisifies all its functions.
If there is only 1 'flow', you can just use a series of then() to complete the process, but sometimes you need to conditionally do other things. Just splitting up these complicated operations in a number of different functions can really help here.
But this:
const result = condition ? Promise.resolve() : Promise.reject();
Is almost always preferred to:
const result = new Promise( (res, rej) => {
if (condition) {
res();
} else {
rej();
}
}
I have an async method named generateSession. And I want the promises system to wait till the call is done. I wait till the data is there and than delete the row at the database. For now, that doesn't make any sense.
But I get an error at this state. Function returned undefined, expected Promise or value It looks like this comes from calling the generateSession. But I don't know how to fix it.
exports.pending = functions.database
.ref('/groups/{groupId}/status/pending/{deviceId}/')
.onCreate(event => {
generateSession().then(function(data) {
console.log("generated session:" + data.sessionId);
return event.data.ref.set({})
}).catch(function(err) {
console.log("error:", err);
});
});
var generateSession = function(){
// *** Return a promise
return new Promise(function(resolve, reject) {
opentok.createSession({mediaMode:"relayed"}, function(err, session){
if (err) {
reject(err);
} else {
resolve(session);
}
});
});
};
The onCreate of Firebase Functions itself needs to return a Promise:
exports.pending = functions.database
.ref('/groups/{groupId}/status/pending/{deviceId}/')
.onCreate(event => {
// You should return the result of generateSession() here
return generateSession().then(function(data) {
console.log("generated session:" + data.sessionId);
return event.data.ref.set({})
}).catch(function(err) {
console.log("error:", err);
// You probably don't want to catch here, let the error
// go through so that Cloud Functions can pick it up
})
});
The code is running well until the Promise.all and then is goes right to the catch saying 'then is not defined'.
I've been trying to figure this out without success for hours :(.
Any help are welcome.
Here is a simplified example of code:
// Save
return new Promise((fulfillSave, rejectSave) => {
// Get
this._getObjects().then((object) => {
var promises = [];
// For each value
object.values.forEach((value) => {
promises.push(
// Create promise
new Promise((fulfill, reject) => {
// Create MDB object + assign value
valueMongoDB.value = value;
// Save
valueMongoDB.save((err, results) => {
if (err) {
reject('Error in saving');
} else {
fulfill();
}
});
})
);
});
// Wait for all promises
Promise.all(promises).then(() => {
// Nothing to do
fulfillSave();
}, then((err) => {
// Err
rejectSave(err);
}));
}
}).catch((err) => {
rejectSave(`Error: ${err.message}`);
});
});
Thanks in advance!
Serge.
This is incorrect:
// Wait for all promises
Promise.all(promises).then(() => {
// Nothing to do
fulfillSave();
}, then((err) => {
// ^^^^--------------------------- error here
// Err
rejectSave(err);
}));
It's trying to call a freestanding function called then and pass its return value into the then on the object returned by Promise.all.
I think you're trying to hook up a failure handler. If so, you don't say then, you just supply a second function:
Promise.all(promises).then(() => {
// Nothing to do
fulfillSave();
}, (err) => {
// Err
rejectSave(err);
}));
But of course, since you're not using the result of that chain and you're just passing the single argument your second function receives into rejectSave, you could just pass rejectSave directly:
Promise.all(promises).then(() => {
// Nothing to do
fulfillSave();
}, rejectSave);
If you told us what your overall code is meant to do and what its inputs are, my suspicion is that that code could be a lot simpler. It's common to create and nest promises unnecessarily, and I suspect that's happening here.
For instance, if you just want to do the saves and get back a promise that will resolve when they're all done successfully or reject on the first failure:
return this._getObjects()
.then(objects => Promise.all(objects.map(value => {
return new Promise((resolve, reject) => {
// Create MDB object + assign value
valueMongoDB.value = value;
// Save
valueMongoDB.save((err, results) => {
if (err) {
reject('Error in saving');
} else {
fulfill();
}
});
});
})));
Or if we give ourselves a helper function for the Mongo bit:
function mongoSavePromise(value) {
return new Promise((resolve, reject) => {
// Create MDB object + assign value
valueMongoDB.value = value;
// Save
valueMongoDB.save((err, results) => {
if (err) {
reject('Error in saving');
} else {
fulfill();
}
});
});
}
then:
return this._getObjects()
.then(objects => Promise.all(objects.map(mongoSavePromise)));
Avoid the Promise constructor antipattern!
Your whole code should be a simple
return this._getObjects().then(object => {
var promises = object.values.map(value => {
// Create MDB object + assign value
valueMongoDB.value = value;
// Save
return valueMongoDB.save().catch(err => {
throw 'Error in saving';
});
});
// Wait for all promises
return Promise.all(promises);
}, err => {
throw `Error: ${err.message}`;
});
No unnecessary callbacks, no room for mistakes. Btw, you shouldn't throw strings.
The second part of the Promise below (inside the then) is never run. When I run the database query without using the Promise(in a node script that I run node myscript.js it returns the data but the console never returns the prompt--the console just hangs and I have to send an interrupt manually. Therefore, when I put it inside a Promise, I think the Promise doesn't know that the database query is complete even though it seems to have returned all the data, therefore the second part of the Promise isn't running ( I think). If that's the problem, how do I write the database query so that it doesn't hang and the Promise can run to completion?
const sqlite = require('/usr/local/lib/node_modules/sqlite3');
const express = require('/usr/local/lib/node_modules/express')
const promise = require('/usr/local/lib/node_modules/promise')
app.get('/', (request, res) => {
var res = [];
function getData() {
return new Promise(function(resolve, reject) {
db.each('SELECT column_a, column_b FROM trips group by column_a', (e, rows) => {
var d = {
a: rows['column_a'],
b: rows['column_b']
}
res.push(d)
});
});
}
getData().then(function(data) {
console.log("never run....", res, data) //never run
});
})
You need to resolve a promise by calling one of the functions it provides in the callback through its constructor.
const promise = new Promise((resolve, reject) => {
// you must call resolve() or reject() here
// otherwise the promise never resolves
});
Otherwise it will always stay in Pending state and never call the callbacks(s) you pass into then.
promise.then(() => {
// this never gets called if we don't resolve() or reject()
});
Additionally, promises allow you to resolve with values so there's usually no need to maintain global variables, you can just pass results through.
Finally, the callback in db.each will be called once for each row, so you would need to handle that by resolving the promise after all rows have been obtained
Here's how you could write your code:
function getData() {
const data = [];
return new Promise((resolve, reject) => {
db.each('SELECT column_a, column_b FROM trips group by column_a', (e, row) => {
if (e) {
// error reading a row, reject the Promise immediately
// optionally you could accumulate errors here in a similar manner to rows
reject(e);
return;
}
// success reading a row, store the row result
data.push({
a: row['column_a'],
b: row['column_b']
});
}, (e, rowCount) => { // the complete handler called when the operation is done, see docs: https://github.com/mapbox/node-sqlite3/wiki/API#databaseeachsql-param--callback-complete
if (e) {
// operation finished, there was an error
reject(e);
return;
}
// operation succeeded, resolve with rows
resolve(data);
});
});
}
app.get('/', (request, res) => {
getData().then((data) => {
// here `data` is an array of row objects
}, (e) => {
console.error(`Database error: ${e}`);
});
});
Side Note
Not sure why you are redeclaring the parameter res as an [], but there's no need for doing var res = []. Since you already have res, you can just say res = [] to point res to a new array. Of course that will overwrite the response object so I assume that you're doing it just for the purposes of this example. If not, you should probably create a new variable.
You've declared a Promise which means you're responsible for calling one of resolve or reject once and once only.
Here's a cleaned up example:
app.get('/', (request, res) => {
var res = [ ];
new Promise((resolve, reject) => {
db.each('SELECT column_a, column_b FROM trips group by column_a', (e, row) => {
if (e) {
reject(e);
return;
}
res.push({
a: row['column_a'],
b: row['column_b']
});
}, (err) => {
if (err) {
return reject(err);
}
resolve(res);
});
}).then((data) => {
console.log("Running ", res, data)//never run
}
});
If your database layer supports promises that usually makes this sort of code a lot less messy since you can simply chain that in there.
Edit: Since the Sqlite3 API is bizarrely non-standard and the each function has two callbacks you need to handle each row with the first, then the completion handler with the second.
If you design an API like this you're doing it wrong. Don't.
Several points :
resolve/reject must be called, otherwise a new Promise() will remain forever "pending".
always promisify at the lowest level possible, ie promisify db.each() not getData(). This gives you a testable, reusable utility and more comprehensible application code.
db.each() is a challenge to promisify because it has two possible sources of error; one in its iteration callback and one in its complete callback.
the sqlite3 documentation does not state what happens if an iteration error occurs but presumably the iteration continues, otherwise the error would simply appear as a completion error?
Here's a couple of ways to promisify :
1. First iteration error or completion error causes promise rejection - iteration errors are not exposed to your application code.
// Promisification
db.eachAsync = function(sql, iterationCallback) {
return new Promise(function(resolve, reject) {
db.each(sql, (iterationError, row) => {
if(iterationError) {
reject(iterationError);
} else {
iterationCallback(row);
}
}, (completionError, n) => {
if(completionError) {
reject(completionError);
} else {
resolve(n); // the number of retrieved rows.
}
});
});
};
// Application
app.get('/', (request, response) => {
function getData() {
var res = [];
return db.eachAsync('SELECT column_a, column_b FROM trips group by column_a', (row) => {
res.push({
a: row['column_a'],
b: row['column_b']
});
}).then(n => res);
}
getData().then(results => {
console.log(results);
}).catch(error => {
console.log(error);
});
});
2. Only a completion error causes promise rejection - iteration errors are exposed to your application code
// Promisification
db.eachAsync = function(sql, iterationCallback) {
return new Promise(function(resolve, reject) {
db.each(sql, iterationCallback, (completionError, n) => {
if(completionError) {
reject(completionError);
} else {
resolve(n); // the number of retrieved rows.
}
});
});
};
// Application
app.get('/', (request, response) => {
function getData() {
var res = [];
return db.eachAsync('SELECT column_a, column_b FROM trips group by column_a', (iterationError, row) => {
// You can choose what to do on iterationError.
// Here, nulls are injected in place of values from the db,
// but you might choose not to push anything.
res.push({
a: iterationError ? null : row['column_a'],
b: iterationError ? null : row['column_b']
});
}).then(n => res);
}
getData().then(results => {
console.log(results);
}).catch(error => {
console.log(error);
});
});
(2) is the better approach because exposing iteration errors affords you more flexibility. For example, you could choose to promisify with (2), and emulate (1) in your application :
// Application
app.get('/', (request, response) => {
function getData() {
var res = [];
var e = null;
return db.eachAsync('SELECT column_a, column_b FROM trips group by column_a', (iterationError, row) => {
if(iterationError && !e) {
// remember the first iteration error
e = iterationError;
} else {
// push only on success
res.push({
a: row['column_a'],
b: row['column_b']
});
}
}).then(n => {
if(e) {
throw e;
} else {
return res;
}
});
}
getData().then(results => {
console.log(results);
}).catch(error => {
console.log(error);
});
});
With (1), by rejecting on first iteration error rather than exposing iteration errors, the same flexibility is not available. (1) could not fully emulate (2).
Fortunately, the preferred approach (2) is the same as would be obtained with Bluebird's .promisify() method :
Promise.promisify(db.each);
I'm struggling to wrap my head around a nested promise layout where one one object is returned at the end of it. My current code is as follows:
router
router.get(`/${config.version}/event/:id?`, function (req, res, next) {
var event = new Event(req, res, next);
event.getInfo(req.params.id).then((info) => {
res.send(info);
});
});
function
getInfo(id) {
db.main('events').where('id', id).select()
.then((result) => {
if(result.length > 0) {
var event = result[0];
//regular functions
event.status = this.getStatus(id);
event.content = this.getContent(id);
event.price = this.getPrice(id);
//promise functions
var users = this.getUsers(id);
var hosts = this.getHosts(id);
Promise.all([users, hosts]).then(values => {
event.users = values[0];
event.hosts = values[1];
//return whole event object to router
return event;
})
.catch((err) => {
return {
result: 'error',
error: err
};
});
} else {
return {
result: 'error',
error: "Event does not exist"
};
}
}).catch((e) => {
return {
result: 'error',
error: "Could not retrieve event info"
};
});
}
As you can see, the router initiates a call to get info about an event. The function then does a database call and gets some event data. Thereafter I need to get the users and hosts of the event from a different table, append that info to the event object as well and then return the whole object to the router to be sent to the client.
When I do this I get an error because I'm not returning a promise from the getInfo function, but I'm not sure how or which promise I'm supposed to return.
I'd appreciate some help with this. Thanks
using .then means that you are returning a promise.
function getInfo(id) {
return new Promise(function(resolve, reject) {
resolve('yay!');
})
}
getInfo().then(function(result) { //result = yay! });
to make your code work, simply replace all the returns with resolves, the errors with rejects, and wrap the whole thing with a return new Promise as i did.
getInfo(id) {
return new Promise(function(resolve, reject) {
db.main('events').where('id', id).select()
.then((result) => {
if (result.length > 0) {
var event = result[0];
//regular functions
event.status = this.getStatus(id);
event.content = this.getContent(id);
event.price = this.getPrice(id);
//promise functions
var users = this.getUsers(id);
var hosts = this.getHosts(id);
Promise.all([users, hosts]).then(values => {
event.users = values[0];
event.hosts = values[1];
//return whole event object to router
resolve(event);
})
.catch((err) => {
reject({
result: 'error',
error: err
});
});
} else {
reject({
result: 'error',
error: "Event does not exist"
});
}
}).catch((e) => {
reject({
result: 'error',
error: "Could not retrieve event info"
});
});
});
}
Just wrap your async code in Promise like this:
getInfo(id) {
return new Promise(function(resolve, reject) {
db.main('events').where('id', id).select()
.then((result) => {
//...
resolve(/* result */)
// OR
reject(/* Error */)
})
}
Note: Use resolve and reject instead return
It's a combination of several things, but the main one is that you are never returning anything from getInfo, so your router handler is calling .then on undefined.
Do not call .catch (without throwing inside it) on Promises you intend to return for a caller to consume. This makes it not possible to use .catch, because you recovered the Promise chain into a resolved one.
Whatever you return inside a .then will be merged into the promise chain, so it's not actually a "Promise that resolves with a Promise". Your whole code could be replaced with:
getInfo (id) {
return db.main('events').where('id', id).select()
.then(result => {
if (result.length == 0) {
// you can also just throw your error object thing,
// but standard Error are generally the convention
throw new Error('Event does not exist')
}
const [event] = result
event.status = this.getStatus(id)
event.content = this.getContent(id)
event.price = this.getPrice(id)
return Promise.all([this.getUsers(id), this.getHosts(id)])
.then(([users, hosts]) => {
event.users = users
event.hosts = hosts
// this is the only value that
// this.getInfo(id).then(value => {/* ... */}) will see
return event
}
})
}