This question already has an answer here:
Promise chaining: Use result from previous promise in next then callback [duplicate]
(1 answer)
Closed 8 years ago.
I'm trying to do a few fetches with Javascript using a Promises API, and then use all of the values I just fetched. Something like...
// Use an Id to find thing 1, use thing 1 Id to find thing2, use thing 2 id to find thing 3,
// then use all of them.
thing1model.findById(thing1id).then(function(thing1) {
return thing2model.findById(thing1.id);
}).then(function(thing2) {
return thing3model.findById(thing2.id);
}).then(function(thing3) {
// Here I want to use all of thing1, thing2, thing3...
someFunction(thing1, thing2, thing3);
}).catch(function(err) {
console.log(err);
});
The problem is thing1 and thing2 go out of scope after the function call. How can I use them in the last then function?
You can save thing1 and thing2 in variables which are declared in scope above your chain.
Like this,
var thing1Data, thing2Data
thing1model.findById(thing1id).then(function(thing1) {
thing1Data = thing1
return thing2model.findById(thing1.id);
}).then(function(thing2) {
thing2Data = thing2
return thing3model.findById(thing2.id);
}).then(function(thing3) {
someFunction(thing1Data, thing2Data, thing3);
}).catch(function(err) {
console.log(err);
});
Related
This question already has answers here:
Promise.all is returning an array of undefined and resolves before being done
(3 answers)
When should I use a return statement in ES6 arrow functions
(6 answers)
Closed 26 days ago.
I have a question. I am currently learning how to use promises but I have run into a problem that I haven't found the answer for after extensive searching. I am trying using map to get an array of promises that are the result of a then block to process in a Promise.all, yet the following does not work, what is going wrong here?
let promises = myArray.map(result =>
{
searchPromise(result.type).then((searchReply) =>
{
return processReply(result, searchReply)
});
}
);
Promise.all(promises).then(c => console.log(c)); //array of Undefined, but
//want to get results from
//processReply function
Your map function is not returning anything, only your then function:
let promises = myArray.map(result =>
{
return searchPromise(result.type).then((searchReply) =>
{
return processReply(result, searchReply)
});
}
);
Alternatively, omit the curly braces as stated in the comments, to force a return:
let promises = myArray.map(result =>
searchPromise(result.type).then((searchReply) => processReply(result, searchReply)
));
You're likely getting an array filled with undefined because your call to myArray.map was not returning anything, thus implicitly returning undefined. Assuming searchPromise and processReply both return a Promise, this should work:
let promises = myArray.map(result => {
return searchPromise(result.type).then((searchReply) => {
return processReply(result, searchReply)
});
});
Promise.all(promises).then(c => console.log(c));
This question already has answers here:
forEach/for...in not returning values? [duplicate]
(5 answers)
Closed 5 years ago.
I've checked other questions that seem to be duplicates but none of them have solved my problem. I have this simple function that loops through an array of rule objects and returns the one with the matching "type":
$ctrl.findRule = function(ruleName){
$ctrl.rules.forEach(function(rule){
if(rule.type === ruleName){
console.log("returning rule: " + rule.type);
return rule;
}
});
return null;
};
I call this function as follows:
var wordCountRule = $ctrl.findRule("word_count");
console.log(wordCountRule);
I see on the console returning rule: word_count and then the console.log(wordCountRule) displays undefined. I have tried everything and I have no idea why this is happening.
Thanks!
The issue is because you're returning the value from the inner forEach handler function, not your outer findRule() function.
To fix this you could define a variable to hold the return value and amend that within the inner scope:
$ctrl.findRule = function(ruleName) {
var returnVal = null;
$ctrl.rules.forEach(function(rule) {
if (rule.type === ruleName) {
returnVal = rule;
}
});
return returnVal;
};
However you should note that it you're looking for a single value you can use find() directly, without the need to loop explicitly:
$ctrl.findRule = function(ruleName) {
return $ctrl.rules.find(function(rule) {
return rule.type === ruleName;
});
};
Taking the above example a step further, by using ES6 syntax it can be reduced to just this:
$ctrl.findRule = ruleName => $ctrl.rules.find(rule => rule.type === ruleName);
This question already has answers here:
How do I access previous promise results in a .then() chain?
(17 answers)
Closed 6 years ago.
I have a promise chain like so
functionOne()
.catch(errorHandlerOne)
.then(functionTwo) // calls functionTwo(responseOne)
.catch(errorHandlerTwo)
.then(functionThree) // calls functionThree(responseTwo)
.catch(errorHandlerThree)
.finally(finalHandler)
This might seem to be an obvious answer, but my question is this:
I can access the responseOne in functionTwo() fine, but how can I access the responseOne in functionThree() or finalHandler()?
Edit: I thought about assigning it to a variable and accessing it later, but it seems to be quite hacky and against the flow of the promise chain. I'm looking for a better way
how can I access the responseOne in functionThree() or finalHandler()?
By passing them forward in some way, as the return value of the then callback (or the resolution value of a promise it returns, which is effectively the same thing).
Example:
function asyncOp(name) {
return new Promise(resolve => {
resolve(name);
});
}
asyncOp("one")
.then(result1 => {
return asyncOp("two")
.then(result2 => [result1, result2]);
})
.then(results => {
console.log("results", results);
});
An array like that is just one option; you could use an object, you could store the interim results in variables the handler close over, ...
Same example in ES5 (just in case someone needs it):
function asyncOp(name) {
return new Promise(function(resolve) {
resolve(name);
});
}
asyncOp("one")
.then(function(result1) {
return asyncOp("two")
.then(function(result2) {
return [result1, result2];
});
})
.then(function(results) {
console.log("results", results);
});
This question already has answers here:
How to access the correct `this` inside a callback
(13 answers)
Closed 6 years ago.
I've found one issue while using Babel that I have not been able to resolve yet.
The thing is that I'm using promises within a class method and I need to access the this object of the class inside the promise to invoke class functions.
The code is
class SecurityService {
constructor($q) {
this.$q = $q;
}
isAuthenticated() {
return true;
}
requestCurrentUser() {
return this.$q.when({});
}
requireAuthenticatedUser() {
let deferred = this.$q.defer();
this.requestCurrentUser()
.then(
function (user) {
if (this.isAuthenticated()) {
this.$log.debug('Security: Access granted for user.');
return deferred.resolve(user);
}
return deferred.reject();
}
);
}
}
When I invoke requireAuthenticatedUser method it fails while executing this.isAuthenticated() because it is trying to find it in the promise scope.
Babel usually wraps the this variable in a upper scope variable, doing something like:
var _this4 = this;
to allow the use in the child scope, but it have checked that only does this thing when using callbacks, but not promises.
Is there any preset or any other thing that I need to import to make it work?
You can manually assign this to a variable before calling the method (the same way you're assigning $q.defer()).
requireAuthenticatedUser() {
let deferred = this.$q.defer();
var securityservice = this;
this.requestCurrentUser()
.then(
function (user) {
if (securityservice.isAuthenticated()) {
This question already has answers here:
How do I access previous promise results in a .then() chain?
(17 answers)
Closed 8 years ago.
From what I understand one of the main selling points for Promises is the ability to write flat code (or, flatter than callback hell).
Though it seems that in many cases we need to nest promises, in order to use closure. For example (from q's docs, though I use Bluebird):
function authenticate() {
return getUsername()
.then(function (username) {
return getUser(username);
})
// chained because we will not need the user name in the next event
.then(function (user) {
return getPassword()
// nested because we need both user and password next
.then(function (password) {
if (user.passwordHash !== hash(password)) {
throw new Error("Can't authenticate");
}
});
});
}
Is there a cleaner way to do this, without nesting?
EDIT: I've managed to clean up this specific example using .all, but there are more complex cases where I don't think it can be done:
function authenticate() {
return Promise.all([
getUsername().then(getUser),
getPassword()
]).spread(function (user, password) {
if (user.passwordHash !== hash(password)) {
throw new Error('Can\'t authenticate');
}
});
}
Yes, you can always flatten a promise chain with Promise.all (shorthand through Promise.join in Bluebird) by using promises for the proxies they are. After all - promises abstract values, you can always unwrap a promise as late as you want and have other variables depend on it.
Whether or not it's more readable is another debate:
foo().then(function(a){
return bar(a).then(function(b){
return g(a,b); // "needed" to nest because I wanted `a`
});
});
Can be written as:
var a = foo();
var b = a.then(bar);
Promise.join(a, b, function(a,b){
return g(a, b); // alternatively, res could have been `Promise.join(a,b, g)`
});
So generally - you can always avoid nesting but a lot of time you might not want to.
In your case, this can even be:
function authenticate() {
var userPass = Promise.all([ getUsername().then(getUser), getPassword()]);
var passHash = userPass.get(0).get("passwordHash");
var newHash = userPass.get(1).then(hash);
var equal = Promise.join(userHash, newHash, function(a, b){ return a !==b });
return equal.then(function(val){ if(!val) throw new Error("..."); });
}
Flatter? Sure. Better? That's a whole other question. If you have a nested for loop, you might want to keep it a nested for loop and nest rather than hack around that option and use a single loop.