I'm using the request-promise module and have found no mention of how to chain requests. I'm currently following their syntax of:
request({options})
.then(function(result){...})
.catch(function(error){...})
However I want to be able to use Promise.all and try to make multiple calls at the same time and wait for them to all resolve and then proceed with other calls. For example I want to:
Make a call to one app creating a User.
AT THE SAME TIME, make a call creating an Address.
Promise.all([UserCall, AddressCall]).then({function to deal with results])?
Also I have been working with my function in module.exports = {...}. Does this require me to be outside of exports and have them declare as separate variables?
From what I understand its seems as if I have to do something like:
var UserCall = function(req,res){
return new Promise(function (resolve, reject){
request({options})? //To make the call to create a new user?
// Then something with resolve and reject
Any help is much appreciated. I think I may be mixing up basic BlueBird concepts and trying to use them with request-promise.
Here you go:
var BPromise = require('bluebird');
var rp = require('request-promise');
BPromise.all([
rp(optionsForRequest1),
rp(optionsForRequest2)
])
.spread(function (responseRequest1, responseRequest2) {
// Proceed with other calls...
})
.catch(function (err) {
// Will be called if at least one request fails.
});
Like you said, you can accomplish this using the all API.
Refer to the documentation here: http://bluebirdjs.com/docs/api/promise.all.html
Example:
var self = this;
return new Promise(function(resolve) {
Promise.all([self.createUser, self.createAddress])done(
// code path when all promises are completed
// OR should any 1 promise return with reject()
function() { resolve(); }
);
})
As noted in the code, the .all() callback code path will get called as well when any 1 of the defined promise in promises gets rejected.
Related
I use some Promise code used in my application as below;
import { Promise, resolve, all } from 'rsvp';
someAction: function(secId, fld, callback) {
var self = this;
var section = self.findSection(self.get('allSecs'), secId);
var myPendingPromise = section.myPendingPromise || resolve();
myPendingPromise = myPendingPromise.then(function(){
return self.myCustomPromise(secId, fld, callback);
});
set(section, 'myPendingPromise', myPendingPromise);
},
myCustomPromise: function(secId, fld, callback){
var self = this;
return new Promise(function(resolve, reject){
var deferred = self.myCustomRule(someFlds); //Makes an API call
deferred.then(function(response) {
resolve(response);
}, function(){
resolve(true);
});
});
},
Now, I am a bit confused why the following lines are added specifically;
var myPendingPromise = section.myPendingPromise || resolve();
myPendingPromise = myPendingPromise.then(function(){
return self.myCustomPromise(secId, fld, callback);
});
set(section, 'myPendingPromise', myPendingPromise);
Also, I did not find "myPendingPromise" used anywhere else apart from this function. Is there some pattern which I need to be aware of to be able to understand this code?
It would be great to understand just the usage of these 3 lines of code above.
What is that
It looks like an attempt to solve concurrency problem by adding all new promises to promise chain (queue). I prepared a simplified example based on your code that demonstrates how it works.
What exactly each line does:
//Extract pending promise from section object. If undefined, use resolve()
//to create and resolve dummy promise:
var myPendingPromise = section.myPendingPromise || resolve();
//Add new promise to chain, so it would start after
//pending promise is resolved:
myPendingPromise = myPendingPromise.then(function(){
return self.myCustomPromise(secId, fld, callback);
});
//Save new promise chain into section object:
set(section, 'myPendingPromise', myPendingPromise);
Why it's a bad solution to concurrency problem (my opinion)
A bit hard to read and understand
If promise takes a long time to finish and someAction is called many times, queue can grow uncontrollably long
It seems that nothing indicates to user that something is running
What is a good solution (again, my opinion)
Use library like ember-concurrency to manage concurrency
Avoid using queue strategy for concurrency problems. If you need to use "queue" strategy, take measures to limit queue length
Add some indication so user sees that button worked and request is happening. It's easy to do using ember-concurrency
I'm new to Nodejs and having trouble understand this issue: I tried to run a describe function against an array, and the AWS function seems to run after the main function has finished.
Here's the main function: (loop thru a list of ACM ARNs and check the status)
var checkCertStatus = function(resolveObj){
var promise = new Promise(function(resolve, reject){
console.log('1');
var retObj='';
resolveObj.Items.forEach(function(element) {
var certDescribeParams = {
CertificateArn: element.sslCertId
};
console.log('2');
acm.describeCertificate(certDescribeParams, function(err, data) {
if(err) reject(new Error(err));
else {
console.log(data.Certificate.DomainName + ': ' + data.Certificate.Status);
retObj+=data;
}
});
});
console.log('3');
resolve(retObj);
return promise;
})
}
Based on the debug log, assuming there are 2 items need to be processed, what I got:
1
2
2
3
example.com: ISSUED
example2.com: ISSUED
Basically, I need to pass this result to the next function in the chain (with promise and stuff).
Welcome to Node.js! Speaking generally, it might be helpful to study up on the asynchronous programming style. In particular, you seem to be mixing Promises and callbacks, which may make this example more confusing than it needs to be. I suggest using the AWS SDK's built-in feature to convert responses to Promises.
The first thing I notice is that you are manually constructing a Promise with a resolve/reject function. This is often a red flag unless you are creating a library. Most other libraries support Promises which you can simply use and chain. (This includes AWS SDK, as mentioned above.)
The second thing I notice is that your checkCertStatus function does not return anything. It creates a Promise but does not return it at the end. Your return promise; line is actually inside the callback function used to create the Promise.
Personally, when working with Promises, I prefer to use the Bluebird library. It provides more fully-featured Promises than native, including methods such as map. Conveniently, the AWS SDK can be configured to work with an alternative Promise constructor via AWS.config.setPromisesDependency() as documented here.
To simplify your logic, you might try something along these lines (untested code):
const Promise = require('bluebird');
AWS.config.setPromisesDependency(Promise);
const checkCertStatus = (resolveObj) => {
const items = resolveObj.Items;
console.log(`Mapping ${items.length} item(s)`);
return Promise.resolve(items)
.map((item) => {
const certDescribeParams = {
CertificateArn: item.sslCertId,
};
console.log(`Calling describeCertificate for ${item.sslCertId}`);
return acm.describeCertificate(certDescribeParams)
.promise()
.then((data) => {
console.log(`${data.Certificate.DomainName}: ${data.Certificate.Status}`);
return data;
});
});
};
We're defining checkCertStatus as a function which takes in resolveObj and returns a Promise chain starting from resolveObj.Items. (I apologize if you are not yet familiar with Arrow Functions.) The first and only step in this chain is to map the items array to a new array of Promises returned from the acm.describeCertificate method. If any one of these individual Promises fails, the top-level Promise chain will reject as well. Otherwise, the top-level Promise chain will resolve to an array of the results. (Note that I included an inessential .then step just to log the individual results, but you could remove that clause entirely.)
Hope this helps, and I apologize if I left any mistakes in the code.
Question:
Is there an "easy" way to cancel ($q-/$http-)promises in AngularJS or determine the order in which promises were resolved?
Example
I have a long running calculation and i request the result via $http. Some actions or events require me to restart the calculation (and thus sending a new $http request) before the initial promise is resolved. Thus i imagine i can't use a simple implementation like
$http.post().then(function(){
//apply data to view
})
because I can't ensure that the responses come back in the order in which i did send the requests - after all i want to show the result of the latest calculation when all promises were resolved properly.
However I would like to avoid waiting for the first response until i send a new request like this:
const timeExpensiveCalculation = function(){
return $http.post().then(function(response){
if (isNewCalculationChained) {return timeExpensiveCalculation();}
else {return response.data;}
})
}
Thoughts:
When using $http i can access the config-object on the response to use some timestamps or other identifiers to manually order the incoming responses. However i was hoping I could just tell angular somehow to cancel an outdated promise and thus not run the .then() function when it gets resolved.
This does not work without manual implementation for $q-promises instead of $http though.
Maybe just rejecting the promise right away is the way to go? But in both cases it might take forever until finally a promise is resolved before the next request is generated (which leads to an empty view in the meantime).
Is there some angular API-Function that i am missing or are there robust design patterns or "tricks" with promise chaining or $q.all to handle multiple promises that return the "same" data?
I do it by generating a requestId, and in the promise's then() function I check if the response is coming from the most recent requestId.
While this approach does not actually cancel the previous promises, it does provide a quick and easy way to ensure that you are handling the most recent request's response.
Something like:
var activeRequest;
function doRequest(params){
// requestId is the id for the request being made in this function call
var requestId = angular.toJson(params); // I usually md5 hash this
// activeRequest will always be the last requestId sent out
activeRequest = requestId;
$http.get('/api/something', {data: params})
.then(function(res){
if(activeRequest == requestId){
// this is the response for last request
// activeRequest is now handled, so clear it out
activeRequest = undefined;
}
else {
// response from previous request (typically gets ignored)
}
});
}
Edit:
On a side-note, I wanted to add that this concept of tracking requestId's can also be applied to preventing duplicate requests. For example, in my Data service's load(module, id) method, I do a little process like this:
generate the requestId based on the URL + parameters.
check in requests hash-table for the requestId
if requestId is not found: generate new request and store promise in hash-table
if requestId is found: simply return the promise from the hash-table
When the request finishes, remove the requestId's entry from the hash-table.
Cancelling a promise is just making it not invoke the onFulfilled and onRejected functions at the then stage. So as #user2263572 mentioned it's always best to let go the promise not cancelled (ES6 native promises can not be cancelled anyways) and handle this condition within it's then stage (like disregarding the task if a global variable is set to 2 as shown in the following snippet) and i am sure you can find tons of other ways to do it. One example could be;
Sorry that i use v (looks like check character) for resolve and x (obvious) for reject functions.
var prom1 = new Promise((v,x) => setTimeout(v.bind(null,"You shall not read this"),2000)),
prom2,
validPromise = 1;
prom1.then(val => validPromise === 1 && console.log(val));
// oh what have i done..!?! Now i have to fire a new promise
prom2 = new Promise((v,x) => setTimeout(v.bind(null,"This is what you will see"),3000));
validPromise = 2;
prom2.then(val => validPromise === 2 && console.log(val));
I'm still trying to figure out a good way to unit test this, but you could try out this kind of strategy:
var canceller = $q.defer();
service.sendCalculationRequest = function () {
canceller.resolve();
return $http({
method: 'GET',
url: '/do-calculation',
timeout: canceller.promise
});
};
In ECMA6 promises, there is a Promise.race(promiseArray) method. This takes an array of promises as its argument, and returns a single promise. The first promise to resolve in the array will hand off its resolved value to the .then of the returned promise, while the other array promises that came in second, etc., will not be waited upon.
Example:
var httpCall1 = $http.get('/api/something', {data: params})
.then(function(val) {
return {
id: "httpCall1"
val: val
}
})
var httpCall2 = $http.get('/api/something-else', {data: params})
.then(function(val) {
return {
id: "httpCall2"
val: val
}
})
// Might want to make a reusable function out of the above two, if you use this in Production
Promise.race([httpCall1, httpCall2])
.then(function(winningPromise) {
console.log('And the winner is ' + winningPromise.id);
doSomethingWith(winningPromise.val);
});
You could either use this with a Promise polyfil, or look into the q.race that someone's developed for Angular (though I haven't tested it).
Tools: JavaScript ES6
I haven't seen a good succinct answer about the syntax of chaining multiple promises to execute in order. I thought this would be a good nail in the coffin question for all promise newbies out there. :)
My Issue is that I want to call this in a synchronous order getPosts--->getThreads--->initializeComplete()
Here is what I am doing.
userPromise.then(getPostsPromise).then(getThreadsPromise).then(initializeComplete());
userPromise is Promise obj I returned from another part of the code
getPostsPromise returns a Promise and makes a fetch to the server for posts
getThreadsPromise returns a Promise and makes a fetch to the server for threads
initializeComplete is a callback to tell my program that it is initialized.
Here is an example of one of the promises in the chain:
var getPostsPromise = function(){
//Firebase is just a simple server I'm using
var firebasePostsRef = new Firebase("https://myfburl.firebaseio.com/posts");
var postsRef = firebasePostsRef.child(localPlace.key);
return new Promise(function(resolve, reject) {
//Below is a Firebase listener that is called when data is returned
postsRef.once('value', function(snap,prevChild) {
var posts = snap.val();
AnotherFile.receiveAllPosts(posts);
resolve(posts);
});
});
}
But initializeComplete() is being called before getPostsPromise and getThreadsPromise have a chance to finish fetching.
Why is that happening and how do I write the promises to execute in order?
initializeComplete is getting called right away because you are invoking it when passing it to then. You have to omit the parentheses, just like you did for getPostsPromise and getThreadsPromise
userPromise.then(getPostsPromise).then(getThreadsPromise).then(initializeComplete);
While yts's answer is correct (the issue is you're invoking initializeComplete instead of passing the function), I'd rather format the calls a bit differently. Having each callback function call the next function is a bit against the design of promises. I'd rather each function return a promise, and then call then on the returned promise:
userPromise
.then(function(){
return getPostsPromise()
}).then(function(){
return getThreadsPromise()
}).then(function(){
return initializeComplete();
});
or to pass the actual returned objects and not have to do any additional intermediate processing:
userPromise
.then(getPostsPromise)
.then(getThreadsPromise)
.then(initializeComplete);
In nodejs, a lot of function calls are implemented as non-blocking function. Sometimes there could be dependencies between these function calls. For example, when user login, I want first get the user type of the user and then according to the user type I can get the tasks which are just assigned this user type. In the non-blocking function, I use nested function call:
$http.post(url_login, login_request)
.success(function(req) {
$http.post(url_get_tasks, req.type)
.success(function(req) {
//update tasks
})
})
A lot of libraries provides non-blocking functions only, for example, the node_redis library. Is there any good way to handle the nested non-blocking function calls to solve the dependency problem.
There is a page dedicated to this exact problem: http://callbackhell.com/
To sum up, you have the option
to use named functions, as codebox suggests
use the async library
or use promises
If I am not mistaken,
you are using the Angular $http service, which should support proper
promise chaining, so you could reduce your code to
$http.post(url_login, login_request)
.then(function(req){
return $http.pos(url_get_tasks, req.type);
})
.then(function(req){
// update tasks
})
.catch(function(err){
// errors from the first AND second call land here
})
You can write a simple wrapper for async functions to turn them into promises:
// this assumes standard node.js callbacks with the
// signature (error, data)
function toPromise(fn){
return function() {
var args = Array.prototype.slice.call(arguments);
var scope = this;
return new Promise(function(resolve, reject){
var cb = function(err, data) {
return err ? reject(err) : resolve(data);
};
args.push(cb);
fn.apply(scope, args);
});
};
}
// turn fs.readdir into a promise
var readdir = toPromise(require('fs').readdir);
readdir('.')
.then(function(files){
console.log(files);
})
.catch(function(err){
console.log('reading files failed');
})
There was another question regarding Angular $http promises which might help you understand them better: here
I'm not 100% sure what you are asking, but if you want to reduce nesting in your code you can move the inner functions out like this:
function success1(req) {
//update tasks
}
function success2(req) {
$http.post(url_get_tasks, req.type).success(success1)
}
$http.post(url_login, login_request).success(success2)
obviously you should pick better names than success1 though