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
Related
In my script I need to make a XMLrequest, get the response from the server (if resolved), parse it and place the parsed result in a variable.
Only later, with this variable I can do lot of stuff.
So basically, I am trying to separate the creation of such parser object from the rest of my code, in order to:
place the above process into a function in an external script which I can call whenever I need.
not having all my code within a .then() method
organize my code into logical files each devoted to a specific task
So I came up with this piece of code (just a sample) to explain my needs.
As I am fairly new to asyncronous programming, would it be fine to do like so, given that the response should be very fast to resovle (or to reject) in my case?
If it's ok, I would then put this inside a separate file to be able to import it and call testing() (or whatever the name will be) from anywhere I need.
function delay(input) {
return new Promise(function(resolve, reject) {
// some async operation here
setTimeout(function() {
// resolve the promise with some value
resolve(input + 10);
}, 500);
});
}
function testing() {delay(5).then(result => {return document.write(result)})};
testing();
EDIT
Ok, so I think I hit the problem thanks to the answer of #DrewReese and the links in the comments.
I am trying to solve this situation: the code above was misleading to understand my point, but I guess tere's no really simple solution.
Look at this code (it's basically the same as the one above, except fot the last three lines):
function delay(input) {
return new Promise(function(resolve, reject) {
// some async operation here
setTimeout(function() {
// resolve the promise with some value
resolve(input + 10);
}, 500);
});
}
function testing() {delay(5).then(result => {return result})};
var test = testing();
document.write(test);
So in this case, I know when I am defining test the output is 'undefined' because the Promise in testing() has not yet resolved.
The problem I'm trying to solve is: is there (if any) a way to define test only when the Promise is resolved without wrapping it in a then() and maybe outputting something different when it's not resolved (like "LOADING...").
I don't know basically if it is possible to check if a variable has a Promise pending and outputting two different values: when it's waiting and when it's resolved/rejected.
Hopefully I was clear enough, otherwise I'll test more and come back with another question if needed.
The whole point of promises (promise chains) is to escape from "nesting hell" by flattening your "chain", so yes, when your promise chain is resolving blocks of code, it is returning a promise which is then-able. I hope this helps illustrate:
someAsyncHttpRequest(parameter) // <- Returns Promise
.then(result => {
// do something with result data, i.e. extract response data,
// mutate it, save it, do something else based on value
...
// You can even return a Promise
return Promise.resolve(mutatedData);
})
.then(newData => { // <- mutadedData passed as parameter
// do new stuff with new data, etc... even reject
let rejectData = processNewData(newData);
return Promise.reject(rejectData);
})
.catch(err => {
console.log('Any caught promise rejection no matter where it came from:', err);
})
.finally(() => {// You can even run this code no matter what});
If you need to use any variable values you set within the chain, then you need to make the outer function async and await on the resolution of the promise chain:
asyncFunction = async (parameter) => {
let asyncValue;
await someAsyncHttpRequest(parameter)
.then(result => {
...
asyncValue = someValue;
...
});
// safe to use asyncValue now
};
So for you:
function delay(input) {
return new Promise(function(resolve, reject) {
// some async operation here
setTimeout(function() {
// resolve the promise with some value
resolve(input + 10);
}, 500);
});
}
**async** function testing() { // Declare this an asynchronous function
let value = **await** delay(5); // Now you can await the resolution of the Promise
console.log(value); // Outputs resolved value 15!
return value; // Just returns resolved Promise
}
var test = testing();
console.log(test);
/** Outputs the Promise!
Promise {<pending>}
__proto__:Promise
[[PromiseStatus]]:"resolved"
[[PromiseValue]]:15
*/
test.then(console.log)); // Outputs returned resolved value, 15
This question already has answers here:
How to synchronize a sequence of promises?
(6 answers)
Closed 5 years ago.
I have a promise chain problem I have been grappling with. I make a call out to an external api that returns me data that I need to process and ingest into a mongo database. I am using nodejs and mongodb with express. Anyway, the calls to the api are working ok, the problem is that I am making tons of them at once. I want to slow them down, like make all the calls for one set. Wait a minute. Make all the calls for the next set. If this was a known amount of sets I would just promise chain them. I dont know how many sets there are so I am looping through them. I think the closure is the problem but cant work around it. on to the example code!
function readApi(carFactory){
var promise = new Promise(function(resolve, reject) {
// call out to api, get set of car data from factory1
console.log(carFactory);
if (true) {
console.log('resolved');
resolve("Stuff worked!"+carFactory);
}
else {
reject(Error("It broke"));
}
});
return promise;
}
function manager(){
//singular call
readApi("this is a singular test").then(returnedThing=>{
console.log(returnedThing); //Stuff worked! this is a singular test
});
let dynamicList = ["carFactory1", "carFactory2","carFactory3","carFactory..N"];
let readApiAction = [];
dynamicList.forEach(carIter=>{
readApiAction.push(readApi(carIter));
});
//ok so now I got an array of promise objects.
//I want to call the first one, wait 1 minute and then call the next one.
//originally I was calling promise.all, but there is no way to get at
//each promise to pause them out so this code is what I am looking to fix
let results= Promise.all(readApiAction);
results.then(data=>{
data.forEach(resolvedData=>{
console.log(resolvedData); //Stuff worked carFactory1, etc...
});
});
//singular call with timeout, this does not work, each one called at the same time
let readApiActionTimeouts = [];
dynamicList.forEach(carIter=>{
setTimeout(callingTimeout(carIter), 6000);
});
}
//just a function to call the promise in a timeout
//this fails with this - TypeError: "callback" argument must be a function
function callingTimeout(carIter){
readApi(carIter).then(returnedThing=>{
console.log("timeout version"+returnedThing);
});
}
A bit on theory. Native Promise.all just groups promises. They're still executed simultaneously (in async way, though, as all JS code, but along each other). This means that it will still congest the API and perform a lot of calls.
Another thing to note is that if you want to delay a promise, you have to delay its return value (e.g. resolve). In order to do so, you may use setTimeout INSIDE new Promise (just look below for more explanation).
Set timeout is asynchronous. It does not work as in other languages (it does not just pause execution). Setting fixed timeout in your code just caused to move ALL the executions by 6s. They still happened in parallel (in different ticks, though, but it's a small difference). Try e.g. generating different timeouts for each one in the loop - you'll notice that they're happening in a different time BUT! This is not a good solution for promisified code!
And now - time for practical answer!
If you use Bluebird, it has a special method to add delay or timeout to each promise. Without it, you would have to write a wrapper around Promise, e.g. resolving it after a particular amount of time and then use it with Promise.all.
First solution (bluebird):
function delayMyPromise(myPromise, myDelay);
return Promise.delay(myDelay).then(function() {
return myPromise;
});
});
and then in your code:
return Promise.all(delayMyPromise(promise1, 1000), delayMyPromise(promise2, 2000)); // Notice different delays, you may generate them programatically
Or even cooler, you can use Promise.map from Bluebird instead of Promise.all that has a special concurrency setting so you may force your promises to be executed in particular sequence, e.g. 2 at a time.
This is how I did it on my previous project :)
More here:
http://bluebirdjs.com/docs/api/promise.delay.html
http://bluebirdjs.com/docs/api/map.html
Pure native Promise implementation:
function delayMyPromise(myPromise, myDelay) {
return new Promise(function (resolve, reject) {
setTimeout(function() {
return resolve(myPromise);
}, myDelay);
});
}
I'd, however, heavily recommend first approach if you don't mind using Bluebird. It's like lodash for Promises and it's really fast :)
you get the error : TypeError: "callback" argument must be a function because your callingTimeout returns nothing and setTimeout needs a function as argument, this is how to fixe it :
let readApiActionTimeouts = [];
dynamicList.forEach(carIter=>{
callingTimeout(carIter)
});
your promise :
function readApi(carFactory){
var promise = new Promise(function(resolve, reject) {
//...
setTimeout(()=>{
resolve("Stuff worked!"+carFactory);
}, 6000);
//...
});
return promise;
}
You could use recursion for something like this.
When you call .forEach, each iteration happens immediately.
In the example below, doSomething is not called until the setTimeout occurs, which means each letter is printed 1 second apart.
let letters = ["a", "b", "c"];
function doSomething(arr) {
console.log(arr[0]);
if (arr.length > 1) {
setTimeout(() => doSomething(arr.slice(1)), 1000);
}
}
doSomething(letters);
Alternately, for your array of promises:
let promises = [
Promise.resolve("A"),
Promise.resolve("B"),
Promise.resolve("C"),
];
function doSomething(arr) {
arr[0].then((result) => {
console.log(result);
if (arr.length > 1) {
setTimeout(() => doSomething(arr.slice(1)), 1000);
}
})
}
doSomething(promises);
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.
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.
Normally if you aren't using promises you can easily do
var a = function(cb){
setTimeout(function(){
var boundCb = cb.bind({hello: 'world'});
boundCb();
}, 100);
};
a(function(){
alert(this.hello);
});
which isn't particularly useful most of the times (as you can pass the same things as normal arguments), but it does allow for some extremely readable code and/or simple to use interfaces.
When switching to promises however it suddenly becomes a lot harder to do. Unsurprisingly the following doesn't work (as resolve isn't a direct reference)
var a = function(){
return new Promise(function(resolve, reject){
var boundResolve = resolve.bind({hello: 'word'});
boundResolve();
});
};
a().then(function(){
alert(this.hello)
});
so is there any way at all to achieve this?
Non essential postscriptum: 'Luckily' in my situation the this scope I want to set is already the same as what the this scope is at a(). So I am currently simply writing .bind(this) after every single Promise callback function and in the future I will be able to use arrow functions for this, but I am looking for a cleaner solution in the interim.
Native Promises do not offer this functionality. However, the Bluebird Promise library offers Promise.prototype.bind
Here's an example usage
MyClass.prototype.method = function() {
return fs.readFileAsync(this.file).bind(this)
.then(function(contents) {
var url = urlParse(contents);
return this.httpGetAsync(url); // this is bound to the MyClass instance
}).then(function(result) {
// this is still bound, further down the chain.
var refined = this.refine(result);
return this.writeRefinedAsync(refined);
}).catch(function(e) {
this.error(e.stack); // Even in event handlers
});
};