I have a chain of promises as a function in my app. Each of my service functions returns a deferred.promise;
Considering the following scenario I have where main getUser service calls getUserPreferences and getUserFavourites asynchronously, the console.log after resolving getUserData is being resolved before getUserFavourites even responds! Shouldn't the promise in getUserData be resolved once getUserFavourites responds?
In fact 'got all user data' from the console.log is in the console before getUserFavourites is called. Literally straight after getUser responds almost like getUserData().then( only resolves the top level promise and make the underlying 2 asynchronous...
What am I doing wrong here?
var user = 'blabla';
function getUserData() {
var deferred = $q.defer();
getUser(user).then(
function(response) {
getUserPreferences(response.user).then(
function(preferences) {
console.log('preferences', preferences);
},
function() {
deferred.reject();
}
);
getUserFavourites(response.user).then(
function(favourites) {
deferred.resolve();
console.log('favourites', favourites);
},
function() {
deferred.reject();
}
);
},
function() {
deferred.reject();
}
);
return deferred.promise;
}
getUserData().then(
function() {
console.log('got all user data');
}
);
A way to fix that is to use the async/await to make the code look synchronous.
var user = 'blabla';
async function getUserData() {
try {
var deferred = $q.defer();
let userInfo = await getUser(user)
let userPrefs = await getUserPreferences(userInfo.user)
console.log('preferences', userPrefs);
let userFavourites = await getUserFavourites(userInfo.user)
deferred.resolve();
console.log('favourites', userFavourites);
return deferred.promise;
} catch (error) {
deferred.reject();
}
}
getUserData().then(
function() {
console.log('got all user data');
}
);
Use $q.all to return a composite promise:
function getUserData() {
return getUser(user).then(function(response) {
var preferencesPromise = getUserPreferences(response.user);
var favouritesPromise = getUserFavourites(response.user);
return $q.all([preferencesPromise, favouritesPromise]);
});
}
Then extract the data from the composite promise:
getUserData().then([preferences, favourites] => {
console.log('got all user data');
console.log(preferences, favourites);
}).catch(function(error) {
console.log(error);
});
The $q.all method returns a single promise that will be resolved with an array/hash of values, each value corresponding to the promise at the same index/key in the promises array/hash. If any of the promises is resolved with a rejection, this resulting promise will be rejected with the same rejection value.
For more information, see
AngularJS $q Service API Reference - $q.all
You must RETURN the nested promise in order to have a chain.
The problem here is you have 2 nested promises so you will need to return a Promise.all (or $q.all in your case) taking an array of the 2 promises returned by getUserPreferences and getUserFavorites:
var user = 'blabla';
function getUserPreferences(){
return new Promise((resolve, reject) => {
setTimeout(() => {
return resolve({color: 'green'});
},500);
});
}
function getUserFavorites(){
return new Promise((resolve, reject) => {
setTimeout(() => {
return resolve([{id: 1, title: 'first favorite'}, {id: 2, title: 'second favorite'}]);
},500);
});
}
function getUser(){
return new Promise((resolve, reject) => {
setTimeout(() => {
return resolve(user);
},500);
});
}
function getUserData() {
return getUser().then(
function(user) {
console.log(user);
var prefPromise = getUserPreferences(user).then(
function(preferences) {
console.log('preferences', preferences);
return preferences;
},
function(error) {
console.log("Error getting preferences");
throw error;
}
);
var favPromise = getUserFavorites(user).then(
function(favourites) {
console.log('favourites', favourites);
return favourites;
},
function(error) {
console.log("Error getting favorites");
throw error;
}
);
return Promise.all([
prefPromise,
favPromise
]);
},
function(err) {
console.log("Error getting user");
throw err;
}
);
}
getUserData().then(
function(results) {
console.log(results);
}
);
Note that for demo purpose I am using es6 Promise instead of angular $q but the spirit is the same:
$q.defer() => new Promise()
$q.all => Promise.all
As the Promise pattern is great to simplify async code and make it look like synchronous code, you can simplify the upper example with something like:
var user = { name: 'blabla'};
function getUserPreferences(user){
return new Promise((resolve, reject) => {
setTimeout(() => {
return resolve({color: 'green'});
},500);
});
}
function getUserFavorites(user){
return new Promise((resolve, reject) => {
setTimeout(() => {
return resolve([{id: 1, title: 'first favorite'}, {id: 2, title: 'second favorite'}]);
},500);
});
}
function getUser(){
return new Promise((resolve, reject) => {
setTimeout(() => {
return resolve(user);
},500);
});
}
function getUserData() {
return getUser()
.then(user => { // user is resolved
// running parallel promises to get user infos:
return Promise.all([
user,
getUserPreferences(user),
getUserFavorites(user)
]);
})
.then(results => {
// wrapping the results into something more semantic:
let userData = results[0];
userData.prefs = results[1];
userData.favs = results[2];
return userData;
});
}
getUserData().then(
function(userData) {
console.log('Final result:');
console.log(userData);
}
);
Related
im trying to write a promise but seems to be missing something. here is my code:
const myPromise = new Promise(() => {
setTimeout(() => {
console.log("getting here");
return setinputs({ ...inputs, images: imageAsUrl });
}, 100);
});
myPromise
.then(() => {
console.log("getting here too");
firebase.database().ref(`collection/${idNode}`).set(inputs);
})
.then(() => {
console.log("all is set");
})
.catch((err) => {
console.log(err);
});
if i run the program, the first part of the promise is executing but all .then() functions arent executing. how do i fix this?
In this scheme, the promise callback has one (resolve) or two (resolve,reject) arguments.
let p = new Promise((resolve, reject)=> {
//do something
//resolve the promise:
if (result === "ok") {
resolve(3);
}
else {
reject("Something is wrong");
}
});
p.then(res => {
console.log(res); // "3"
}).catch(err => {
console.error(err); //"Something is wrrong
});
Of course, nowadays you can use async + await in a lot of cases.
You need to resolve the promise, using resolve() and also return the promise from firebase so the next .then in the chain works properly.
const myPromise = new Promise((resolve, reject) => {
setTimeout(() => {
console.log("getting here");
// You have to call resolve for all `.then` methods to be triggered
resolve({ ...inputs, images: imageAsUrl });
}, 100);
});
myPromise
.then((inputs) => {
console.log("getting here too");
// You have to return a promise in a .then function for the next .then to work properly
return firebase.database().ref(`collection/${idNode}`).set(inputs);
})
.then(() => {
console.log("all is set");
})
.catch((err) => {
console.log(err);
});
I am new to promises and trying to figure out for quite a long time now how to get proper results after the usage of a async network call with which I receive data.
I receive my balance from a exchange and loop through several parameters. When this is finished the holdings should be returned.
However, I still have to fight the async behaviour. When I run the code without the commented code, the result is []. If I set a artificial setTimeout, then the returned array holdings is visible properly.
Can someone tell me please where my mistake lays? I tried to read through docs of mdn and similar problems here on stackoverflow but I am nonetheless stuck.
Thank you guys very much,
Tobias
const bittrex = require('node.bittrex.api');
const {key, secret} = require('./key')
let getBalance = new Promise((resolve, reject) => {
let holdings = [];
bittrex.getbalances( function( data, err ) {
if (err) {
reject(err);
}
data.result.forEach(coin => {
if (coin.Balance !== 0) {
let market = `BTC-${coin.Currency}`;
if(coin.Currency === 'BTC') market = `USDT-BTC`;
bittrex.getticker( { market : market}, function( ticker, err ) {
if (err) {
reject(err);
}
holdings.push({
Coin: coin.Currency,
Balance: coin.Balance,
Last: ticker.result.Last
});
})
}
});
});
resolve(holdings);
})
getBalance
// .then((holdings) =>{
// return new Promise((resolve, reject) => {
// setTimeout(() => {
// resolve(holdings);
// }, 10000)
// })
// })
.then((holdings) => {
console.log(holdings);
})
You're resolving your promise instantaneously but the data is not here yet, as it is happened asynchronously during the callback. Your promise should be resolved after every callback.
What you should do is create a promise for each of the request and then resolve your function with a Promise.all
const bittrex = require('node.bittrex.api');
const {key, secret} = require('./key')
let getBalance = new Promise((resolve, reject) => {
let holdings = [];
bittrex.getbalances( function( data, err ) {
if (err) {
reject(err);
}
const promises = data.result.map(coin => new Promise((resolve, reject) => {
if (coin.Balance !== 0) {
let market = `BTC-${coin.Currency}`;
if(coin.Currency === 'BTC') market = `USDT-BTC`;
bittrex.getticker( { market : market}, function( ticker, err ) {
if (err) {
reject(err);
}
resolve({
Coin: coin.Currency,
Balance: coin.Balance,
Last: ticker.result.Last
});
})
}
});
resolve(Promise.all(promises));
});
});
Your getBalance promise will be resolved when all of your promise are resolved. Be cautious though, if one of your promise is rejected, then the whole promise will be rejected.
If it's properly resolved, then the value will be an array of each value of the promises.
Since I'm assuming bittrex.getticker is async, you should instead just wrap each call into a promise and not try to combine them into one manually.
Here's a loose concept.
function getTicker(...) {
return new Promise(function(resolve, reject) {
bittrex.getticker(..., function(ticker, error) {
if (error) { reject(error); }
else { resolve(ticker); }
});
});
}
var lotsOfPromises = data.result.map(function(coin) {
return getTicker(...).then(function(ticker) {
return { yourOutputdata }
});
});
Promise.all(lotsOfPromises);
I currently try to use two kind of promises. One for the overall convensation, and one to get the userids to usernames.
The code works when its ran first time, and is looking something like this:
The output above is what i wanted.
But when i run the query again, i get this:
what is wrong with my code? what do i need to do to make the promises go correctly?
code:
function getconvensations(userid) {
return new Promise(function(resolve, reject) {
convensations.find({members: userid}).lean().then(function (convensations) {
var promises = convensations.map(function (currentConvensation) {
return new Promise(function (resolve, reject) {
var realnames = [];
var usernames = currentConvensation.members.map(function (CurrentUser) {
Core.userDetails(CurrentUser).then(function (user) {
realnames.push(user.username);
console.log("RESOLVING USERNAMES");
resolve();
});
});
Promise.all(usernames).then(function () {
console.log("GET LATEST MESSAGE");
latestMessage(currentConvensation._id).then(function (message) {
console.log("Messages: ");
console.log(message);
currentConvensation.spesificmessage = message;
currentConvensation.users = realnames;
currentConvensation.otherend = (currentConvensation.members[0] == userid ? realnames[0] : realnames[1]);
console.log("RESOLVE LATEST MESSAGE");
resolve();
});
});
});
});
Promise.all(promises).then(function () {
console.log("FINISHED CONVENSATIONS");
console.log("-------------------------");
console.log(convensations);
console.log("-------------------------");
return resolve(convensations);
}).catch(console.error);
});
});
}
caller:
} else if(action == 'getconvensations') {
Messages_model.getconvensations(req.user._id).then(function (response) {
res.json(response);
});
}
You have a race condition here:
new Promise(function (resolve, reject) {
…
currentConvensation.members.map(function (CurrentUser) {
Core.userDetails(CurrentUser).then(function (user) {
resolve();
});
});
…
latestMessage(currentConvensation._id).then(function (message) {
resolve();
});
…
});
There are arbitrarily many tasks executing concurrently, and the first promise that fulfills will call resolve().
The solution is to avoid the Promise constructor antipattern and never ever call new Promise or resolve manually! Instead, chain promise callbacks to each other using the then method, and return new promises from every function to allow the caller to wait for them.
function getconvensations(userid) {
return convensations.find({members: userid}).lean().then(function (convensations) {
// ^^^^^^
var promises = convensations.map(function (currentConvensation) {
var usernamepromises = currentConvensation.members.map(function (CurrentUser) {
console.log("GETTING USERNAME");
return Core.userDetails(CurrentUser).then(function (user) {
// ^^^^^^
console.log("FULFILLED USERNAME");
return user.username;
// ^^^^^^
});
});
return Promise.all(usernamepromises).then(function (realnames) {
// ^^^^^^
console.log("FULFILLED ALL USERNAMES");
currentConvensation.users = realnames;
currentConvensation.otherend = (currentConvensation.members[0] == userid ? realnames[0] : realnames[1]);
console.log("GETTING LATEST MESSAGE");
return latestMessage(currentConvensation._id);
// ^^^^^^
}).then(function (message) {
console.log("FULFILLED LATEST MESSAGE");
console.log("Message: ", message);
currentConvensation.spesificmessage = message;
});
});
return Promise.all(promises).then(function () {
// ^^^^^^
console.log("FINISHED ALL CONVENSATIONS");
console.log("-------------------------");
console.log(convensations);
console.log("-------------------------");
return convensations;
// ^^^^^^
});
});
}
I am working in a Node.js app with Q promise library. I have two set of promise chains and one is for controlling the flow and one for calling service methods where I retrieve data from, My question is, I need to get the return value of the promise chain to my other promise chain.
MyExample.js
bookService.getBookById(bookId)
.then(bookDetals)
.then(function(returnValue) { // <- (A)
res.send(200, returnValue); // <- (C)
return returnValue;
}).catch(function(error) {
logger.error('Error getting values');
res.send(500, error);
});
bookDetals = function(book) {
myService.retrieveATypeData(book, bookId)
.then(function(bookData) {
myService.retrieveBTypeData(bookId)
.then(function(bdata) {
bookData[bTypeData] = bdata;
myService.retrieveCTypeData(bookId)
.then(function(cdata) {
bookData[cTypeData] = cdata;
}).done(function() {
return bookData; // <- (B)
})
});
});
};
In the above code, I am calling bookService.getBookById(bookId) and getting the book. Then I am calling bookDetals function which is a promise chain. But my problem is it returns the returnValue before the promise chains over. How can I get the return value of promise chain (in line (B)) to return in place (C). Currently it return before. so in place C it says undefined.
Since you are using Node, I would move towards ES6 Promises. If your current version does not yet support ES6 Promises, I would recommend you switch over to a library (es6-promise) that polyfills it for you. With ES6, you could do something like this:
// mock async promise
const getThing = id => (
new Promise((resolve, reject) => {
setTimeout(() => {
resolve({
id
});
}, 250);
})
);
// mock async promise
const getDetailsA = thing => {
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve(Object.assign({}, thing, {
a: 'purple'
}));
}, 250);
})
};
// mock async promise
const getDetailsB = thing => (
new Promise((resolve, reject) => {
setTimeout(() => {
resolve(Object.assign({}, thing, {
b: 'monkey'
}));
}, 250);
})
);
// mock async promise
const getDetailsC = thing => (
new Promise((resolve, reject) => {
setTimeout(() => {
resolve(Object.assign({}, thing, {
c: 'dishwasher'
}));
}, 250);
})
);
getThing('123')
.then(getDetailsA)
.then(getDetailsB)
.then(getDetailsC)
.then(console.log)
.catch(console.error);
You need to return a promise:
bookDetals = function(book) {
return Q.Promise(function(resolve, reject, notify) {
myService.retrieveATypeData(book, bookId)
.then(function(bookData) {
myService.retrieveBTypeData(bookId)
.then(function(bdata) {
bookData[bTypeData] = bdata;
myService.retrieveCTypeData(bookId)
.then(function(cdata) {
bookData[cTypeData] = cdata;
}).done(function() {
resolve(bookData); // <- (B)
})
});
});
}
}
edit:
deferred is an anti-pattern discussed here. Honestly, it might be best to use a polyfill since Promise is in the es6 spec.
I would like to create a function that returns a promise and if something throws an error within, it returns promise reject.
function promiseFunc(options) {
return new Promise(() => {
return options;
});
}
function myfunc(options) {
return new Promise(() => {
if (!options) throw new Error("missing options");
return promiseFunc(options).then((result) => {
if (result.throwerr) throw new Error("thrown on purpose");
return result.value;
});
});
};
My test as follows:
const myfunc = require("./myfunc");
describe('myfunc', () => {
it('should fail without options', () => {
return myfunc()
.then((result) => { throw new Error(result) }, (err) => {
console.log("test #1 result:", err.message === "missing options");
});
});
it('should fail on options.throwerr', () => {
return myfunc({throwerr: true})
.then((result) => {}, (err) => {
console.log("test #2 result:", err.message === "thrown on purpose");
});
});
it('should return options.value', () => {
return myfunc({value: "some result", throwerr: false})
.then((result) => {
console.log("test #3 result:", result === "some result");
}, (err) => {});
});
});
The first test pass, but the second and third fails.
Log #2 does not even run, so I assumed the "throw on purpose" messes up something, therefore I created test #3, where I don't throw anything, but it still fails.
What am I missing?
Solution:
function promiseFunc(options) {
return new Promise(resolve => {
return resolve(options);
});
}
function myfunc(options) {
return new Promise((resolve, reject) => {
if (!options) throw new Error("missing options");
return promiseFunc(options).then(result => {
if (result.throwerr) throw new Error("thrown on purpose");
return resolve(result.value);
}).catch(err => {
return reject(err);
});
});
};
You forgot to pass a function with resolve and reject parameters, so your promises just don't work.
function promiseFunc(options) {
return new Promise(resolve => { // resolve function
resolve(options)
})
}
module.exports = function myfunc(options) {
return new Promise((resolve, reject) => { // since you may either resolve your promise or reject it, you need two params
if (!options) {
return reject(new Error("missing options"))
}
return promiseFunc(options).then(result => {
if (result.throwerr) {
return reject(new Error("thrown on purpose"))
}
resolve(result.value)
})
})
}
... and the test (mocha)
const assert = require('assert'),
myfunc = require("./myfunc")
describe('myfunc', () => {
it('should fail without options', done => { // mind the callback, promises are always async
myfunc()
.catch(err => {
assert(err.message === "missing options")
done() // <- called here
})
})
it('should fail on options.throwerr', done => {
myfunc({throwerr: true})
.catch(err => {
assert(err.message === "thrown on purpose")
done()
})
})
it('should return options.value', done => {
return myfunc({value: "some result", throwerr: false})
.then(result => {
assert(result === "some result")
done()
})
})
})
I would like to create a function that returns a promise and if something throws an error within, it returns promise reject.
This will do it ...
var q = require('q'); // In recent versions of node q is available by default and this line is not required
function iReturnAPromise(num) {
var def = q.defer();
if (typeof num=== 'number') {
try {
var value = 100 / num;
def.resolve(value);
} catch(e) {
def.reject("oops a division error - maybe you divided by zero");
}
} else {
def.reject("o no its not a number");
}
return def.promise;
}
PS this function was coded freehand and has not been tested - but this will work. Obviously try catch should be used sparingly.
PS I prefer the q library implementation of promise instead of the default node promise library - they take a very different approach. q dispenses with all the wrapping!
using the promise library u wanted ...
function iReturnAPromise(num) {
return new Promise(function(resolve, reject) {
if (typeof num === 'number') {
try {
var value = 100 / num;
resolve(value);
} catch (e) {
reject("oops a division error - maybe you divided by zero");
}
} else {
reject("o no its not a number");
}
})
}
iReturnAPromise(7).then(
function(response) {console.log("success", response)},
function(response) {console.log("failure", response)}
);
// Unexpectedly this is not an error in node 5.6 because div by 0 is not an error operation anymore!
iReturnAPromise(0).then(
function(response) {console.log("success", response)},
function(response) {console.log("failure", response)}
);
iReturnAPromise("fred").then(
function(response) {console.log("success", response)},
function(response) {console.log("failure", response)}
);
you can see why i prefer the q syntax :)