I am trying to pass an object that I build piece by piece with promises using firebase. I don't really need the object to be passed along the promise chain if there is a better way to construct the object step by step. Here is my code:
var queue = new Queue(productUpdateQueue, function(data, progress, resolve, reject) {
var incomingUpdateData = data;
var receiptID = incomingUpdateData.receiptID;
var userID = incomingUpdateData.userID;
var oldProductID = incomingUpdateData.oldProductID;
var newProductID = incomingUpdateData.newProductID;
var newReceipt = incomingUpdateData.newReceipt;
var postID = "";
var updateObject = {};
updateObject['usersPrivate/'+userID+'/receipts/'+receiptID+'/items/'+oldProductID] = null;
updateObject['usersPrivate/'+userID+'/receipts/'+receiptID+'/items/'+newProductID] = newReceipt;
clicks.child('VigLink').orderByChild('item').equalTo(oldProductID).once('value', function(cuidSnapshot) {
return cuidSnapshot.forEach(function(cuidSnapshot) {
var cuid = cuidSnapshot.key;
updateObject['clicks/VigLink/'+cuid+'/item'] = newProductID;
console.log('one');
progress(20);
});
}).then(function() {
return userReceiptMetrics.child(userID).child('receipts').child(receiptID).child('items').child(oldProductID).once('value', function(oldSnapshot) {
var data = oldSnapshot.val()
updateObject['userReceiptMetrics/'+userID+'/receipts/'+receiptID+'/items/'+oldProductID] = null
updateObject['userReceiptMetrics/'+userID+'/receipts/'+receiptID+'/items/'+newProductID] = data
if (data != null) {
updateObject['userReceiptMetrics/'+userID+'/receipts/'+receiptID+'/itemIDs/'+newProductID] = now
updateObject['userReceiptMetrics/'+userID+'/receipts/'+receiptID+'/itemIDs/'+oldProductID] = null
};
console.log('two');
progress(40);
});
}).then(function() {
return userReceiptMetrics.child(userID).child('shops').child(oldProductID).once('value', function(oldSnapshot) {
var data = oldSnapshot.val()
updateObject['userReceiptMetrics/'+userID+'/shops/'+oldProductID] = null;
updateObject['userReceiptMetrics/'+userID+'/shops/'+newProductID] = data;
if (data != null) {
updateObject['userReceiptMetrics/'+userID+'/shopIDs/'+newProductID] = now;
updateObject['userReceiptMetrics/'+userID+'/shopIDs/'+oldProductID] = null;
};
console.log('three');
progress(60);
});
}).then(function() {
return posts.once('value', function(postSnapshot) {
// use Promise.all and Array#map to wait for all these queries to finish
var allPosts = postSnapshot.val()
var postKeys = Object.keys(allPosts)
return Promise.all(postKeys.map(function(postKey) {
var postID = postKey;
return posts.child(postID).child('items').child(oldProductID).once('value', function(itemSnapshot) {
itemSnapshot.forEach(function(itemSnapshot) {
var itemData = itemSnapshot.val()
console.log('post snapshot'+ itemSnapshot);
updateObject['posts/'+postID+'/items/'+oldProductID] = null
updateObject['posts/'+postID+'/items/'+newProductID] = itemData
});
});
})).then(function(results) {
// put progress update in .then, and return the results
progress(75);
return results;
});
});
}).then(function() {
// Move to next item
return console.log('hey look here'+updateObject['posts/'+postID+'/items/'+newProductID]);
return firebaseRoot.update(updateObject, function(error) {
if (error) {
console.log("Error updating data:", error);
reject()
} else {
progress(100);
// resolve();
console.log('four');
}
});
});
// Finish the task asynchronously
setTimeout(function() {
reject();
}, 10000);
});
And the output is:
one
two
three
hey look hereundefined
post snapshot[object Object]
post snapshot[object Object]
post snapshot[object Object]
post snapshot[object Object]
post snapshot[object Object]
post snapshot[object Object]
post snapshot[object Object]
post snapshot[object Object]
Any and all help is super appreciated.
You need to change your .then from (now changed in the question, but the code originally had this)
}).then(function(updateObject) {
to
}).then(function() {
So that your code always updates the updateObject declared in the new Queue callback
also, the first line in each then needs to have a return added
so, instead of
userReceiptMetrics.child(userID).child('receipts').child(receiptID).child('items').child(oldProductID).once('value', function(oldSnapshot) {
you have
return userReceiptMetrics.child(userID).child('receipts').child(receiptID).child('items').child(oldProductID).once('value', function(oldSnapshot) {
The progress calls need to be put at the end of the .once callbacks
over all, the code should look like (removed most of the code that's correct, hope this is understandable
var queue = new Queue(productUpdateQueue, function(data, progress, resolve, reject) {
// removed for brevity
var updateObject = {};
// removed for brevity
clicks.child('VigLink').orderByChild('item').equalTo(oldProductID).once('value', function(cuidSnapshot) {
// removed for brevity
progress(12);
}).then(function() {
return userReceiptMetrics.child(userID).child('receipts').child(receiptID).child('items').child(oldProductID).once('value', function(oldSnapshot) {
// removed for brevity
progress(25);
});
}).then(function() {
return userReceiptMetrics.child(userID).child('shops').child(oldProductID).once('value', function(oldSnapshot) {
// removed for brevity
progress(50);
});
}).then(function() {
return posts.orderByChild('items').equalTo(oldProductID).once('value', function(postSnapshot) {
// use Promise.all and Array#map to wait for all these queries to finish
return Promise.all(postSnapshot.map(function(postSnapshot) {
var postID = postSnapshot.key;
return posts.child(postID).child('items').child(oldProductID).once('value', function(itemSnapshot) {
itemSnapshot.forEach(function(itemSnapshot) {
var itemData = itemSnapshot.val()
updateObject['posts/'+postID+'/items/'+oldProductID] = null
updateObject['posts/'+postID+'/items/'+newProductID] = itemData
});
});
})).then(function(results) {
// put progress update in .then, and return the results
progress(75);
return results;
});
});
}).then(function() {
// Move to next item
console.log(updateObject);
// removed for brevity
});
// Finish the task asynchronously
setTimeout(function() {
reject();
}, 10000);
});
So I finally figured out how to do this. I was having a problem passing the object from step to step but what I really wanted to do was create the object step by step. So I created the object outside of the promise chain and built it step by step. One of the main obstacles here was to get my loops to complete and then move on to the next promise in the chain. In order to do that, I used Promise.all() to return the results.
It works and now I know more about how promises work. If this can help you please take it:
var queue = new Queue(productUpdateQueue, function(data, progress, resolve, reject) {
var incomingUpdateData = data;
var receiptID = incomingUpdateData.receiptID;
var userID = incomingUpdateData.userID;
var oldProductID = incomingUpdateData.oldProductID;
var newProductID = incomingUpdateData.newProductID;
var newReceipt = incomingUpdateData.newReceipt;
var postID = "-KZOO0UII67uOmYo6DJh";
var postKeys = [];
var updateObject = {};
updateObject['usersPrivate/'+userID+'/receipts/'+receiptID+'/items/'+oldProductID] = null;
updateObject['usersPrivate/'+userID+'/receipts/'+receiptID+'/items/'+newProductID] = newReceipt;
return clicks.child('VigLink').orderByChild('item').equalTo(oldProductID).once('value', function(cuidSnapshot) {
return cuidSnapshot.forEach(function(cuidSnapshot) {
var cuid = cuidSnapshot.key;
updateObject['clicks/VigLink/'+cuid+'/item'] = newProductID;
progress(10);
});
}).then(function() {
return userReceiptMetrics.child(userID).child('receipts').child(receiptID).child('items').child(oldProductID).once('value', function(oldSnapshot) {
var data = oldSnapshot.val()
updateObject['userReceiptMetrics/'+userID+'/receipts/'+receiptID+'/items/'+oldProductID] = null
updateObject['userReceiptMetrics/'+userID+'/receipts/'+receiptID+'/items/'+newProductID] = data
if (data != null) {
updateObject['userReceiptMetrics/'+userID+'/receipts/'+receiptID+'/itemIDs/'+newProductID] = now
updateObject['userReceiptMetrics/'+userID+'/receipts/'+receiptID+'/itemIDs/'+oldProductID] = null
};
progress(25);
});
}).then(function() {
return userReceiptMetrics.child(userID).child('shops').child(oldProductID).once('value', function(oldSnapshot) {
var data = oldSnapshot.val()
updateObject['userReceiptMetrics/'+userID+'/shops/'+oldProductID] = null;
updateObject['userReceiptMetrics/'+userID+'/shops/'+newProductID] = data;
if (data != null) {
updateObject['userReceiptMetrics/'+userID+'/shopIDs/'+newProductID] = now;
updateObject['userReceiptMetrics/'+userID+'/shopIDs/'+oldProductID] = null;
};
progress(40);
});
}).then(function() {
progress(55);
return posts.orderByChild('receipt').equalTo(receiptID).once('value');
}).then(function(postSnapshot) {
return postSnapshot.forEach(function(post) {
progress(70);
postKeys.push(post.key)
});
}).then(function() {
return Promise.all(postKeys.map(function(postKey) {
return posts.child(postKey).child('items').child(oldProductID).once('value', function(itemSnapshot) {
var itemData = itemSnapshot.val()
updateObject['posts/'+postKey+'/items/'+oldProductID] = null;
updateObject['posts/'+postKey+'/items/'+newProductID] = itemData;
});
})).then(function(results) {
progress(85);
return results;
});
}).then(function() {
return firebaseRoot.update(updateObject, function(error) {
if (error) {
console.log("Error updating data:", error);
reject()
} else {
progress(100);
resolve();
}
});
});
// Finish the task asynchronously
setTimeout(function() {
reject();
}, 10000);
});
Related
I am having trouble in using promises in javascript
Actually I am trying to start the second function when the first function ends as the first function performs some operations on data and in the second function this data will be used further
Here is the first function which loads the data and this data later to be used in the second function
update_users: function() {
fetch(this.users_url)
.then(
function(response) {
return response.json();
}
)
.then(
(data) => {
this.data.users = data;
let event = new CustomEvent("modelUpdated", {detail: this});
window.dispatchEvent(event);
}
);
},
Here is the second function
get_user: function(userid) {
var promise = new Promise(function(resolve, reject) {
// do a thing, possibly async, then…
console.log("Reached");
update_users();
if ( true) {
resolve("Stuff worked!");
}
else {
reject(Error("It broke"));
}
});
promise.then(function(result) {
let check = 0;
for(let i = 0; i < this.data.users.length; i++)
{
console.log(".." + i);
if(this.data.users[i].id == userid)
{
console.log("This was the id we were talking about.");
console.log(this.data.users[i]);
check = check + 1;
return this.data.users[i];
}
}
if (check = 0)
{
return null;
}
console.log(result); // "Stuff worked!"
}, function(err) {
console.log(err); // Error: "It broke"
});
},
Here is the error
ReferenceError: update_users is not defined
at model.js:250
at new Promise (<anonymous>)
at Object.get_user (model.js:244)
at main.js:40
I am trying to get a bunch of ID's from an API and then form a sequence of requests which would make further calls to an API to fetch some parameters. These would be totaled and i expect the output results to be pushed as JSON array.
The problem is REST call is async and i've put a promise but not sure when to resolve the promise back to the calling function, the rest call some times take a second or 2 to respond back.
I would like know at what point can i resolve the promise or how to know when the totals have been computed ?
The Route
app.get("/sonar/:x_id",function(req,resp) {
getRestSonar(req.params.x_id).then(function (fromResolve) {
resp.send(fromResolve);
});
});
The function with promise which makes the rest call loops
var getRestSonar = function(requestX) {
return new Promise(function(resolve,reject) {
var unirest = require("unirest");
var reqx = unirest("GET", "http://sonarqubexxServer/api/projects");
var outputJson = {
table: []
};
reqx.end(function (res) {
if (res.error) throw new Error(res.error);
// console.log(res.body);
var result = res.body;
//var needle = req.params.csi_id;
var needle = requestX;
var TotalDuplicateLines = 0;
var TotalBugs = 0;
var TotalNcloc = 0;
var TotalCodeSmells = 0;
var TotalVulnerabilities = 0;
for (var i=0;i<result.length;i++) {
if (result[i].nm.indexOf(needle) !== -1) {
console.log(result[i].k);
var queryUrl = "http://sonarqubexxServer/api/resources?resource="+result[i].k+"&metrics=code_smells,bugs,vulnerabilities,ncloc,coverage,duplicated_lines&format=json"
console.log(queryUrl);
var subrequest = unirest("GET",queryUrl);
subrequest.end(function (resXX) {
if (resXX.error);
var resXXResult = resXX.body;
for (var i=0;i<resXXResult.length;i++) {
// var duplicateData = resXXResult[0].msr.filter(item => item.key == 'duplicated_lines');
resXXResult[i].msr.forEach(m => {
if (m.key === 'duplicated_lines') {
console.log('Duplicated Lines ' + m.val);
TotalDuplicateLines += m.val;
}
else if(m.key === 'bugs' ) {
console.log('Bugs ' + m.val);
TotalBugs += m.val;
}
else if(m.key === 'ncloc' ) {
console.log('Lines of Code ' + m.val);
TotalNcloc += m.val;
}
else if(m.key === 'code_smells' ) {
console.log('Code Smells ' + m.val);
TotalCodeSmells += m.val;
}
else if(m.key === 'vulnerabilities' ) {
console.log('Vulnerabilities ' + m.val);
TotalVulnerabilities += m.val;
outputJson.table.push({totduplines:TotalDuplicateLines},{totVul:TotalVulnerabilities});
}
});
console.log("Iam here with I :: " + i);
if (i === (resXXResult.length - 1)) {
//Should i resolve here makes no sense
console.log("Resolved the promise now..");
}
//The for ends here
}
// I see this is a bad place to resolve..
resolve(outputJson);
});
}
}
});
});
}
EDIT : As suggested in the comments, split the calls into smaller
sections
Now, i fetch the api calls seperatly create an array out of it, then use promises to call back to the API ? how do i resolve each call by looping over it ?
When i try to loop it always resolves request[0] and then comes out of the promise, how can i create a promise array and wait for them to complete ?
app.get("/sonar/:csi_id",function(req,resp) {
var collectiveResult = [];
getRestSonar(req.params.csi_id).then(function (fromResolve) {
return splitReqUrl(fromResolve);
}).then(function(fromSplitUrl) {
console.log("I am from split url ::::" + fromSplitUrl);
return getSubSonarProperties(fromSplitUrl);
}).then(function(fromsubSonar) {
collectiveResult.push(fromsubSonar);
console.log("+++++++++++++++++++++++++++");
console.log(fromsubSonar);
resp.send(collectiveResult);
});
});
var getSubSonarProperties = function(getUrl) {
return new Promise(function(resolve,reject) {
var getSubRest = require("unirest");
console.log("Attempting to GET " + getUrl);
var req = getSubRest("GET",getUrl);
var outputJson = {
table: []
}
var TotalDuplicateLines = 0;
var TotalBugs = 0;
var TotalNcloc = 0;
var TotalCodeSmells = 0;
var TotalVulnerabilities = 0;
req.end(function (res) {
if (res.error);
var resXXResult = res.body;
resolve(resXXResult);
});
});
}
var splitReqUrl = function(request) {
return new Promise(function(resolve,reject) {
resolve(request[1]);
//for(var i=0; i< request.length; i++) {
// resolve(request[i]);
//}
});
}
var getRestSonar = function(requestX) {
return new Promise(function(resolve,reject) {
var unirest = require("unirest");
var reqx = unirest("GET", "http://sonarqubexxx/api/projects");
var outputJson = {
table: []
};
reqx.end(function (res) {
if (res.error) throw new Error(res.error);
// console.log(res.body);
var result = res.body;
//var needle = req.params.csi_id;
var needle = requestX;
var queryArray = [];
for (var i=0;i<result.length;i++) {
if (result[i].nm.indexOf(needle) !== -1) {
console.log(result[i].k);
var queryUrl = "http://sonarxxx/api/resources?resource="+result[i].k+"&metrics=code_smells,bugs,vulnerabilities,ncloc,coverage,duplicated_lines&format=json"
//console.log(queryUrl);
queryArray.push(queryUrl);
}
if (i === (result.length - 1)) {
resolve(queryArray);
}
}
});
});
}
Problem
First of all the problem with your solution is that you're trying to make everything inside a single big new Promise(...) creator.
Even if you manage to make that work it's still a common anti-pattern as Promises are made to be chained using the .then(...) method.
As pointed out by Roamer-1888 there oughta be a fork of unirest that handles Promises directly instead of requiring callbacks as in your example, but let's stick with your version of unirest here.
Solution
So what you need to be doing is create a Promise chain to handle the different steps of your code and pass the results down the chain.
Your steps seem to be:
Make the first call to retrieve initial results.
Filter the results based on the requestX input.
For each item left, make several calls to obtain more data.
Put everything back into an outputJson object.
Basically the only async steps are 1 and 3, but it might be ok to add a third step to build your outputJson and pass it downstream.
So let's start with the first step.
1. Make the first call
In the first link of the Promise chain we need to retrieve the initial results with your first unirest call:
new Promise((resolve, reject) => {
unirest("GET", "http://sonarqubexxServer/api/projects")
.end((res) => {
if (res.error) {
reject(res.error);
} else {
resolve(res.body);
}
});
})
See in this example I already checked if the response contains an error and fired a rejection in that case, otherwise I resolve the promise with the body (the data we need).
The Promise we created above will throw an error if the request fails, and will downstream the body of the response if everything goes fine.
2. Filtering and Sub-calls
Now then we can go ahead and use the full potential of Promises with the .then(...) method:
new Promise((resolve, reject) => {
unirest("GET", "http://sonarqubexxServer/api/projects")
.end((res) => {
if (res.error) {
reject(res.error);
} else {
resolve(res.body);
}
});
}).then((results) => {
results = results.filter((result) => {
return result.nm.indexOf(request) != -1;
});
return Promise.all(results.map((result) => {
return new Promise((resolve, reject) => {
var queryUrl = "http://sonarqubexxServer/api/resources?resource=" + result.k + "&metrics=code_smells,bugs,vulnerabilities,ncloc,coverage,duplicated_lines&format=json"
unirest("GET", queryUrl)
.end((res) => {
if (res.error) {
reject(res.error);
} else {
resolve(res.body);
}
});
})
}))
})
In this step I used some Array methods to make the code cleaner and Promise.all to handle several promises together.
Array.filter is a method which iterates an array and checks for each item if it should be kept in the filtered output or not. So, in your case, we want to keep only those items where result.nm.indexOf(request) != -1.
Array.map is a method which iterates an array and converts each item to something else. Basically the function you provide takes each item as input, converts it to something else and then replaces this new value to the old one in the output array.
Finally Promise.all accepts an array of Promises and returns a Promise itself. This returned Promise will resolve when all the given Promises resolve and will pass downstream an array which items are the results of each single Promise.
So by writing Promise.all(results.map((results) => { return new Promise(...) })) we convert each result in the results array into a Promise that executes the result-specific call and put it into the output array of Promises which is fed to Promise.all so they get executed at once.
3. Build the outputJSON
Now the Promise chain outputs the result of Promise.all which is an array of all the results of each Promise, which are the results of each sub-call.
We can then simply take the downstream data and use your nested iterations to build the outputJSON to be passed downstream:
new Promise((resolve, reject) => {
unirest("GET", "http://sonarqubexxServer/api/projects")
.end((res) => {
if (res.error) {
reject(res.error);
} else {
resolve(res.body);
}
});
}).then((results) => {
results = results.filter((result) => {
return result.nm.indexOf(request) != -1;
});
return Promise.all(results.map((result) => {
return new Promise((resolve, reject) => {
var queryUrl = "http://sonarqubexxServer/api/resources?resource=" + result.k + "&metrics=code_smells,bugs,vulnerabilities,ncloc,coverage,duplicated_lines&format=json"
unirest("GET", queryUrl)
.end((res) => {
if (res.error) {
reject(res.error);
} else {
resolve(res.body);
}
});
})
}))
}).then((allResults) => {
var TotalDuplicateLines = 0;
var TotalBugs = 0;
var TotalNcloc = 0;
var TotalCodeSmells = 0;
var TotalVulnerabilities = 0;
var outputJson = {
table: []
};
for (var i = 0; i < allResults; i++) {
for (var j = 0; j < allResults[i].length; j++) {
allResults[i][j].msr.forEach(m => {
if (m.key === 'duplicated_lines') {
TotalDuplicateLines += m.val;
}
else if (m.key === 'bugs') {
TotalBugs += m.val;
}
else if (m.key === 'ncloc') {
TotalNcloc += m.val;
}
else if (m.key === 'code_smells') {
TotalCodeSmells += m.val;
}
else if (m.key === 'vulnerabilities') {
TotalVulnerabilities += m.val;
outputJson.table.push({ totduplines: TotalDuplicateLines }, { totVul: TotalVulnerabilities });
}
});
}
}
return outputJson;
})
If your return this long Promise chain in your getRestSonar(request) function, then you could write getRestSonar(request).then((outputJson) => { ... do something with your outputJson ... })
I'm using angular.js with C# web service and I need to increment ng-repeat item by item to show to the user as the data is updated, to do this I'm trying to use $http.get within loop for refresh data in each item. But its not working
for (var i = 0; i < conditions.length; i++) {
var configFullAsset = {
params: {
Field: Field,
SAM_ConnectionString: SAM_ConnectionString,
TreeElemId: conditions[i][0],
ConditionDate: conditions[i][1]
}
};
$http.get('application.asmx/getExtFullAssetHealth', configFullAsset)
.success(function (responseExt) {
console.log("Element: ", i);
if (i == 0)
{
$scope.showData = responseExt;
$scope.fullAssetTable_loading = false;
$scope.fullAssetTable_loaded = true;
}
else
$scope.showData = $scope.showData.concat(responseExt);
//console.log("Data item: ", $scope.showData[i].Tag);
$scope.fullData = $scope.showData;
$scope.filterData(customFilter);
})
.catch(function (err) {
console.log("Error get object: ", err);
})
.finally(function () {
// Hide loading spinner whether our call succeeded or failed.
//$scope.loading = false;
$scope.fullData = $scope.showData;
$scope.filterData(customFilter);
$scope.fullAssetTable_loading = false;
$scope.fullAssetTable_loaded = true;
console.log($scope.fullData);
});
}
The main problem into your code is the fallowing: you use i as index in the success method but is not what you expected because the loop is over until your first success will be called.
You can build the requests like this in the first phase, is much easier to read:
function buildRequests() {
return conditions.map(function(condition) {
var configFullAsset = {
params: {
Field: Field,
SAM_ConnectionString: SAM_ConnectionString,
TreeElemId: condition[0],
ConditionDate: condition[1]
}
};
return $http.get('application.asmx/getExtFullAssetHealth', configFullAsset);
});
}
Than you can handle all the requests like this:
function handleRequests() {
var requests = buildRequests();
$q.all(requests)
.then(handleRequests)
.catch(function(error) {
console.log(error);
})
.finally(function() {
$scope.fullData = $scope.showData;
$scope.filterData(customFilter);
$scope.fullAssetTable_loading = false;
$scope.fullAssetTable_loaded = true;
});
}
Than to iterate over each result to make the changes:
function handleResults(results) {
results.forEach(function(response, i) {
console.log("Element: ", i);
if (i == 0)
{
$scope.showData = response;
$scope.fullAssetTable_loading = false;
$scope.fullAssetTable_loaded = true;
}
else
$scope.showData = $scope.showData.concat(response);
//console.log("Data item: ", $scope.showData[i].Tag);
$scope.fullData = $scope.showData;
$scope.filterData(customFilter);
});
}
Do not forget to inject $q as dependency injection.
I am working on some async code that queries the github API. each API call I'm making runs through this code and I hit the 'weeksOfYear' console log. What I am trying to do is bundle all of these api calls into one big array so I can work on all the data together. I believe this would involve a callback, but I'm not sure where to put it. I've included my code below.
I believe it is returning this way because it is all inside of one promise. When I try to add another promise to the code, I get an error that says 'then is undefined.' This is because I need to return a promise object. So, I think the solution would be to use a callback.
Thanks!
$.ajax({
method: 'GET',
url: 'https://api.github.com/orgs/lodash/repos' ,
contentType: 'application/json',
beforeSend: function(xhr){
return xhr.setRequestHeader("Authorization", "token adbfde986475bbee66b2e22b4375be4d34adc098")
}
})
.then(function (data){
// console.log('GOT THIS DATA', data);
return data.map(function(val){
return val.name;
});
})
.then(function (newData){
newData.forEach(function(repoName){
pagination(repoName, function(storage) {
storage = storage.filter(function(PR){
if(PR.merged_at !== null){
return true;
}
})
var weeksOfYear = {};
console.log('STORAGE HERE IS', storage);
storage.filter(function(mergedPR){
if (moment(mergedPR.merged_at).year() === 2016){
return true;
}
}).forEach(function(mergedPR){
var weekMerged = moment(mergedPR.merged_at).week();
if(!weeksOfYear[weekMerged]){
weeksOfYear[weekMerged] = 1;
// weeksOfYear[yearMerged] = yearMerged
}else{
weeksOfYear[weekMerged] += 1;
// weeksOfYear[moment(mergedPR.merged_at).year()] = moment(mergedPR.merged_at).year()
}
})
console.log('weeks of Year are', weeksOfYear)
})
})
})
This is what pagination looks like:
var pagination = function (repoName, callback) {
var num = 1;
var storage = [];
function recursiveHelper(repoName, num){
$.ajax({
method: 'GET',
url: 'https://api.github.com/repos/lodash/' + repoName + '/pulls?state=closed&page=' + num,
contentType: 'application/json',
beforeSend: function(xhr){
return xhr.setRequestHeader("Authorization", "token adbfde986475bbee66b2e22b4375be4d34adc098")
}
})
.then(function(numPullRequests){
// console.log('numPullRequests', numPullRequests);
storage = storage.concat(numPullRequests);
if(numPullRequests.length !== 30){
return callback(storage);
}
recursiveHelper(repoName, num + 1);
})
// console.log('this value is', numPullRequests);
}
recursiveHelper(repoName, num)
}
Ok, since pagination is asynchronous, and doesn't return a promise (even though it uses promises! but it's recursive nature makes it what it is) minimal change to your existing code yields this
...
.then(function(newData) {
// use Array#map and Promise.all ...
return Promise.all(newData.map(function(repoName) {
// create a Promise
return new Promise(function(resolve, reject) {
pagination(repoName, function(storage) {
storage = storage.filter(function(PR) {
if (PR.merged_at !== null) {
return true;
}
})
var weeksOfYear = {};
console.log('STORAGE HERE IS', storage);
storage.filter(function(mergedPR) {
if (moment(mergedPR.merged_at).year() === 2016) {
return true;
}
}).forEach(function(mergedPR) {
var weekMerged = moment(mergedPR.merged_at).week();
if (!weeksOfYear[weekMerged]) {
weeksOfYear[weekMerged] = 1;
// weeksOfYear[yearMerged] = yearMerged
} else {
weeksOfYear[weekMerged] += 1;
// weeksOfYear[moment(mergedPR.merged_at).year()] = moment(mergedPR.merged_at).year()
}
});
console.log('weeks of Year are', weeksOfYear);
// resolve the promise in the callback
resolve(weeksOfYear);
});
});
}));
}).then(function(result) {
// results is an array of weeksOfYear
});
However, if you change pagination to return a promise, like this
var pagination = function(repoName) {
var num = 1;
var storage = [];
function recursiveHelper(repoName, num) {
return $.ajax({
method: 'GET',
url: 'https://api.github.com/repos/lodash/' + repoName + '/pulls?state=closed&page=' + num,
contentType: 'application/json',
beforeSend: function(xhr) {
return xhr.setRequestHeader("Authorization", "token adbfde986475bbee66b2e22b4375be4d34adc098")
}
})
.then(function(numPullRequests) {
// console.log('numPullRequests', numPullRequests);
storage = storage.concat(numPullRequests);
if (numPullRequests.length !== 30) {
return storage;
}
return recursiveHelper(repoName, num + 1);
});
// console.log('this value is', numPullRequests);
}
return recursiveHelper(repoName, num);
};
your code can be changed to this
...
.then(function(newData) {
// again, use Array#map and Promise.all
return Promise.all(newData.map(function(repoName) {
// no need for new Promise as pagination returns a promise
return pagination(repoName)
.then(function(storage) {
storage = storage.filter(function(PR) {
if (PR.merged_at !== null) {
return true;
}
})
var weeksOfYear = {};
console.log('STORAGE HERE IS', storage);
storage.filter(function(mergedPR) {
if (moment(mergedPR.merged_at).year() === 2016) {
return true;
}
}).forEach(function(mergedPR) {
var weekMerged = moment(mergedPR.merged_at).week();
if (!weeksOfYear[weekMerged]) {
weeksOfYear[weekMerged] = 1;
// weeksOfYear[yearMerged] = yearMerged
} else {
weeksOfYear[weekMerged] += 1;
// weeksOfYear[moment(mergedPR.merged_at).year()] = moment(mergedPR.merged_at).year()
}
});
console.log('weeks of Year are', weeksOfYear);
// just return weeksOfYear
return weeksOfYear;
});
}));
}).then(function(result) {
// results is an array of weeksOfYear
});
note: if you don't have Promise available
replace
return Promise.all(newData.map(function(repoName) {
with
return $.when.apply($, newData.map(function(repoName) {
and
}).then(function(result) {
// results is an array of weeksOfYear
});
with
}).then(function() {
// arguments will be weeksOfYear
});
For the first answer, you'll also need to change
return new Promise(function(resolve, reject) {
pagination(repoName, function(storage) {
//... snip
console.log('weeks of Year are', weeksOfYear);
// resolve the promise in the callback
resolve(weeksOfYear);
});
});
to
var d = $.Deffered();
pagination(repoName, function(storage) {
//... snip
console.log('weeks of Year are', weeksOfYear);
// resolve the promise in the callback
d.resolve(weeksOfYear);
});
return d.promise();
in a Node.js App, i want to achieve this:
read an array, depend of item type, decide to use an specific function that returns a Q Promise object. i want this process runs sequentially.
i have this two Promises:
var q = require('q');
Classes.prototype.fn1 = function (item) {
return q.Promise(function(resolve, reject){
Items.find({item_published: true}).exec(
function (err, events) {
if (err) {
reject('an error happened');
throw err;
}else{
resolve(events);
}
});
});
};
Classes.prototype.fn2 = function (item) {
return q.Promise(function(resolve, reject){
resolve(item);
});
};
And this is main code:
self.items = [{},{},{}]; //some items
self.result = [];
self.items.forEach(function(item,index,array){
if(item.type == 1){
self.fn1(item)
.then(function(result){
self.result.push(result);
})
}
if(item.type == 2){
self.fn2(item)
.then(function(result){
self.result.push(result);
})
}
if(index == array.length-1){
callback(self.result);
}
});
But it does not work. because that fn1 has a Async process, it runs after fn2. All i want is running these functions sequentially even one of them has Async process.
You can use .reduce to chain the promises.
var promise = q(); // Create a Resolved promise for chaining.
self.items = [{},{},{}]; //some items
self.result = [];
// We put an resolved promise as init value for chaining
self.items.reduce(function(chain, item) {
// Don't do anything if item type is not match
if (item.type !== 1 && item.type !== 2) {
return chain;
}
var targetFunc = null;
if (item.type === 1) {
targetFunc = self.fn1;
} else if (item.type === 2) {
targetFunc = self.fn2;
}
if (targetFunc === null) {
return chain;
}
// Chain the promise and return the last of the chain.
return chain
.then(function(){
return targetFunc(item);
})
.then(function(result){
// This then will get the result from above
// so we put the result to self.result here
self.result.push(result);
});
}, promise).then(function() {
// When all promises are sequentially resolved,
// call the callback with self.resul.
callback(self.result);
});
jsfiddle, jsfiddle-Q.js ver.
Had something very similar to below cooking ... but fuyushimoya's just too fast, though we handle reduce's initialization differently
var promises = self.items.map( function(item) {
if (item.type == 2) {
return self.fn1(item);
}
else if (item.type == 3) {
return self.fn2(item);
}
});
function handler(p) {
return p.then( function(res) {
self.result.push(res);
});
}
promises
.reduce( function(prev, current) {
if (prev) {
return prev.then( function() { handler(current) } )
}
else {
return handler(current)
}
})
.then(function(result) {
callback(null, result);
})
.catch( // error handler);
Sketching it out. But something like this might work.
UPD: the trick was to chain promises as it was mentioned in comments, there is updated version that I used for code snippet here:
var items = [{type:1},{type:2},{type:1}];
var result = [];
var rq = Q();
var ql = rq;
items.forEach(function (it, ix) {
ql = ql.then(function(){
var dp = "fn" + it.type;
return ps[dp]();
})
.then(function(d) {
result.push(d);
});
});
ql.then(function() {
callback(result);
});