I'm attempting to run a Parse.Query in my matchCenterComparison function, which is part of the main promise chain below.
When I run this code, it logs out ('setup query criteria, about to run it'); and ('MatchCenterComparison Succeeded bro!'), but not the console.log within userCategoryThingQuery.find().then.
I've researched this online, and looked through the Parse.Query Documentation, and my conclusion is that the main promise chain isn't waiting for userCategoryThingQuery to finish, since it's asynchronous. Is this what's causing the problem? If so, how can I fix this?
Main Promise Chain:
Parse.Cloud.job("MatchCenterBackground", function(request, status) {
// ... other code to setup usersQuery ...
var usersQuery = new Parse.Query(Parse.User);
usersQuery.each(function (user) {
return processUser(user).then(function(eBayResults){
return matchCenterComparison(eBayResults);
});
}).then(function() {
status.success("background job worked brah!");
}, function(error) {
status.error(error);
});
});
matchCenterComparison Function:
function matchCenterComparison(eBayResults) {
console.log('eBayResults are the following:' + eBayResults);
var matchCenterComparisonPromise = new Parse.Promise();
if (eBayResults.length > 0) {
// do some work, possibly async
console.log('yes the ebay results be longer than 0');
var userCategoryThing = Parse.Object.extend("userCategory");
var userCategoryThingQuery = new Parse.Query(userCategoryThing);
userCategoryThingQuery.contains('categoryId', '9355');
console.log('setup query criteria, about to run it');
userCategoryThingQuery.find().then(function(results) {
console.log('lets see what we got here:' + results);
});
matchCenterComparisonPromise.resolve(console.log('MatchCenterComparison Succeeded bro!'));
} else {
matchCenterComparisonPromise.reject({ message: 'No work done, expression failed' });
}
return matchCenterComparisonPromise;
}
Your problem is here:
function(request, status) {
// ... other code to setup usersQuery ...
var usersQuery = new Parse.Query(Parse.User);
usersQuery.each(function (user) {
return processUser(user).then(function(eBayResults){
return matchCenterComparison(eBayResults);
});
})
Here's a question - what does this function return?
Answer - it returns undefined. It doesn't return a promise, and therefore the chain has nothing to wait on.
What you need to do is take all the promises from your loop over usersQuery and return a promise that doesn't complete until they all do. Try rewriting like this:
function(request, status) {
// ... other code to setup usersQuery ...
var usersQuery = new Parse.Query(Parse.User);
return usersQuery.each(function (user) {
return processUser(user).then(function(eBayResults){
return matchCenterComparison(eBayResults);
});
}))
Looking at the docs for Parse.Query, the important bits are this:
If the callback returns a promise, the iteration will not continue
until that promise has been fulfilled.
and
Returns: {Parse.Promise} A promise that will be fulfilled once the
iteration has completed.
So this should get you what you want - the usersQuery.each call will return a promise that completes when the iteration ends, and returning the promise from inside the callback will mean the iteration doesn't complete until after all the items have been processed.
Related
I promises and I cannot find anything that handles my scenario.
I used a sample set of promises that work perfect until I add the pouch allDocs call in.
It works fine when I chain directly on the allDocs call. I was told I could put a promise in a promise but that does not seem to be the case.
The reason I have it set up the way I do and why it would be difficult to use Promise.All() is because I don't want the promises executing all at onece. I want one promise to follow another.
I stripped everything out only showing one promise that has PouchDB call (allDocs) in it. Maybe this cannot be done.
Here is my promise with console.logs in them so you can see the path.
My promise:
let cleanRoom = function () {
return new Promise(function (resolve, reject) {
console.log("starting cleanRoom");
console.log("starting DB_WorkIssue.alldocs");
DB_WorkIssues.allDocs({ include_docs: true, descending: false }, function (err, response) {
data = response.rows;
itemp = 0;
console.log("at For Loop in allDocs");
for (var i = 0; i < response.total_rows; i++) {
if (data[i].doc.IsDeleted || data[i].doc.IsWorkIssueInserted || data[i].doc.IsWorkIssueUpdated || data[i].doc.IsLogInserted) {
DirtyFlag = true;
}
}
console.log("exiting allDocs");
return;
}).then(function () {
console.log("inside then function after alldocs");
}).catch(function (err) {
console.log("inside catch");
showMsg("Error in cleanRoom/allDocs: " + err);
});
console.log("exiting cleanRoom")
resolve('Cleaned The Room');
});
};
All it does is call the allDocs method and then in the function look to see if any records have been updated. Then it can continue on.
But that is not what is happening.
Here is the chain:
}).then(function () {
return initialize();
}).then(function (prMessage) {
**return cleanRoom();** <--- my promise
}).then(function (result) {
return removeGarbage(result);
}).then(function (result) {
return winIcecream(result);
}).then(function (result) {
console.log('finished ' + result);
}).then(function () {
console.log("starting UpdateworkIssuesJson");
return updateWorkIssuesJson();
}).then(function () {
the results:
tasksmain.js:165 starting cleanRoom
tasksmain.js:166 starting DB_WorkIssue.alldocs <-- Pouch call
tasksmain.js:188 exiting cleanroom <-- exits promise
tasksmain.js:195 inside removeGarbage <-- I don't want it to do this until Pouch call is done.
tasksmain.js:202 inside winIceCream
taskspouch.js:91 finished Cleaned The Room remove Garbage won Icecream
taskspouch.js:93 starting UpdateworkIssuesJson
tasksmain.js:182 inside then function after alldocs <-- now back to the pouch function call (way too late.
tasksmain.js:171 at For Loop in allDocs
tasksmain.js:178 exiting allDocs <--- Pouches chain
taskspouch.js:96 starting showTasks
The question is - the original promise chain is working as it is supposed to.
It continues on when the "cleanroom" resolves. But the cleanroom resolves before the Pouch command is finished. It actually finishes when the allDoc
Is there a way to prevent that?
You forgot to resolve with the inner promise.
Just do resolve(DB_WorkIssues.allDocs({ include_docs: true, descending: false },…) instead of resolving the promise with a string (which immediately resolves the promise).
Ok, I'm fighting hard this problem. I've spent a lot of time on the past week trying to figure out how to make this work. I've learned promises and made some cool stuff - and I'm love with it. But, I can't make this work.
I'm using Mongoose Model.find() method. This methods receives two arguments: an object that will be used to the query and a callback function that will receive (error, data) objects. I'm calling .find and passing findUser function.
UserModel.find(userObj, findUser)
.then(userFound, createUser);
Inside findUser, I'm creating a Promise and resolving or rejecting it.
var findUser = function(err, data) {
var p1 = new Promise( function (resolve, reject) {
if (data.length) {
console.log('Resolved on findUser');
resolve();
} else {
console.log('Rejected on findUser');
reject();
}
});
};
But, whatever happens on findUser, the success callback is always called. On the console, I can see something like this:
Rejected on findUser
Resolved on find
var userFound = function () {
console.log('Resolved on find');
};
var createUser = function () {
console.log('Rejected on find');
}
How could I propagate the promise value from findUser to .find?
You can try something like this. All changes in your code are commented.
// Remove err argument
var findUser = function(data) {
// Instead of creating a new Promise,
// we return a value to resolve
// or throw a value to reject.
if (data.length) {
console.log('Resolved on findUser');
// Resolve this Promise with data.
return data;
} else {
var reason = 'Rejected on findUser';
console.log(reason);
// Reject this Promise with reason.
throw reason;
}
};
// Function gets data passed to it.
var userFound = function(data) {
console.log('Resolved on find');
};
// Function gets reason passed to it.
var createUser = function(reason) {
console.log('Rejected on find');
};
// Promises will chain and propagate errors as necessary.
UserModel.find(userObj)
.then(findUser)
.then(userFound)
.catch(createUser);
I am seeking for an architecture advice. Using Bluebird Promises in a MEAN environment (talking node.js server-side here), I intend to make many concurrent API calls, aggregate all results and respond to client. Example (pseudo-)code:
exports.getAllData = function(searchquery, cb) {
var results;
wrapper1.getResultsFromAPI1(searchquery, function(err,data){
results += data;
});
wrapper2.getResultsFromAPI2(searchquery, function(err,data){
results += data;
});
wrapper3.getResultsFromDataBase(searchquery, function(err,data){
results += data;
});
if (AllRequests done){
cb(null,results);
}
}
Now I don't know how I can make sure to:
Fire all requests concurrently (NOT sequentially to reduce
response time)
Respond to client once I got responses from ALL API requests
In case of one API request to fail for whatever reason, not having the entire promise chain to be rejected, thus "loosing" the other API response data.
I checked on Bluebird Promise website for appropriate collections, but none seems to fully meet the requirements listed above. Any suggestions?
One way of doing this would be using reflect calls.
var Promise= require('bluebird');
Promise.props({
"wrapper1": someasync(1).reflect(),
"wrapper2": someasync(0).reflect(),
"wrapper3": someasync(1).reflect()
})
.then(function(results) {
Object.keys(results).forEach(function(key) {
if (results[key].isRejected()) {
console.log(key + " failed.", results[key].reason());
} else {
console.log(key + " successed", results[key].value());
}
});
});
function someasync(t) {
if (t===0) return Promise.reject('some err');
else return Promise.resolve(true);
}
Which results in the following:
wrapper1 successed true
wrapper2 failed. some err
wrapper3 successed true
var Promise= require('bluebird');
var chain = require('lodash').chain;
function rejectedPromise(settledPromise) {
return settledPromise.isRejected();
}
function extractResponse(fulfilledPromise) {
return fulfilledPromise.value();
}
Promise.settle([
asyncCall(),
asyncCall(),
asyncCall()
])
.then(function retrieveSuccessfulResponses(settledPromises) {
return chain(settledPromises)
.reject(rejectedPromise)
.map(extractResponse)
});
If you want, you can manually promisify your API methods, e.g:
var p1 = new Promise(function(resolve, reject) {
wrapper1.getResultsFromAPI1(searchquery, function(err, data) {
if (err) reject(err);
else resove(data);
});
});
But since you're using the BlueBird library, then you can use Promise.promisify, so you can avoid the boilerplate code of wrapping the methods into promises. E.g:
var getResultsFromAPI = Promise.promisify(wrapper1.getResultsFromAPI1);
var p1 = getResultsFromAPI(searchquery);
And if you want to promisify all the methods of an API, you can use Promise.promisifyAll. E.g:
var wrapper1 = Promise.promisifyAll(require('my-api'));
// then, all the wrapper1 methods are already wrapped into a Promise
var p1 = wrapper1.getResultsFromAPI1(searchquery);
So, after turning all your methods into Promises, you can use the Promise.settle to achieve what you want: See what promises were fulfilled and what of them were rejected:
exports.getAllData = function(searchquery, cb) {
/* Don't forget to promisify all the API methods firstly */
var results;
var p1 = wrapper1.getResultsFromAPI1(searchquery);
var p2 = wrapper2.getResultsFromAPI2(searchquery);
var p3 = wrapper3.getResultsFromDataBase(searchquery);
Promise.settle([p1, p2, p3]).then(function(arr) {
arr.forEach(function(res, index) {
if (res.isFulfilled()) { // check if the Promise was fulfilled
results += res.value(); // the Promise's return value
}
else if (res.isRejected()) { // check if the Promise was rejected
console.log(res.reason()); // do something with the error
}
});
cb(null, results);
});
}
Here is my code, it loops through forEach and prints out '1' but never returns from object.save() & never prints out 2, 3 or anything else. I have tried a bunch of other ways but none seems to work.
Note: response.succes(or error) is not being called anywhere, the code is definitely waiting for object.save() to be completed.
var promise = new Parse.Promise();
var query = new Parse.Query("SomeClass");
query.find().then(function(results) {
var promises = [];
results.forEach(function(object) {
object.set("SomeColumnName", true);
console.log('1');
promises.push(object.save(null, {
success: function(result) {
alert('2');
return ;
},
error: function(result, error) {
alert('3');
return ;
}
}));
});
Parse.Promise.when(promises).then(function() {
console.log('inside resolve');
promise.resolve();
}, function() {
console.log('inside reject');
promise.reject();
});
});
return promise;
You're on the right track, but you should take advantage of the fact that most of the sdk functions create and return promises for you. With those, you can substantially simplify the code:
// very handy utility library that provides _.each among many other things
// www.underscorejs.org
var _ = require('underscore');
// answer a promise to modify all instances of SomeClass
function changeSomeClass() {
var query = new Parse.Query("SomeClass");
// if there are more than 100 rows, set query.limit up to 1k
return query.find().then(function(results) { // find returns a promise
_.each(results, function(result) {
result.set("SomeColumnName", true);
});
return Parse.Object.saveAll(results); // and saveAll returns a promise
});
}
Wrap it in a cloud function and call success/error like this:
Parse.Cloud.define("changeSomeClass", function(request, response) {
changeSomeClass().then(function(result) {
response.success(result);
}, function(error) {
response.error(error);
});
});
You can only have one Parse request happening at a time for each object. If multiple requests are sent, all but the first are ignored. You're probably trying to do this while other threads are making Parse requests for those objects. I know that if you save an object, it also saves it's child objects, so you could be hitting a problem with that. Make sure you do as much as you can in background threads with completion blocks, or use saveEventually / fetchEventually where possible.
Background: I have a PHP background and this is my first application using MEAN stack.
I need to save a record but before I must to check if there is any record under the same id already saved in the DB.
In PHP I would do something like this:
Once the user clicks "Save":
1) Call the function to check if an entry with that id already exists
2) If it doesnt, call the save function.
In Javascript, I'm getting a little confused with Promises and so on.
Can somebody give me some light here?
Right now, I'm doing the following:
In the save api, I call this function to check if the record already exists in the DB:
recordExists = findTranscationByBill(billId);
function findTransactionByBill(billId){
results = new promise(function(resolve, reject){
Transactions.find({billId : billId},function(err, transactions){
if(err)
reject("Error: "+err);
//console.log(transactions);
resolve(transactions);
});
});
results.then(function(data){
console.log('Promise fullfilled: '+ data);
}, function(error){
console.log('Promise rejected: ' + error);
});
return $results;
}
The problem is that I think I'm not using promise properly, as my variable doesn't get populated (because its Async).
In the console.log I see that the promise is being fulfilled however, the variable returns as [object Object]
I'm stucked with this problem because I don't know if I should carry on thinking as PHP mindset or if there is a different approach used in Javascript.
Thanks in advance!
In my opinion you could just as well use a callback for this, and since MongoDB has a count method, why not use it
function findTransactionByBill(billId, callback){
Transactions.count({billId : billId}, function(err, count){
if (err) {
callback(err, false);
} else {
callback(null, count !== 0);
}
});
}
and to use it
findTransactionByBill(billId, function(err, exists) {
if (err) {
// handle errors
} else if ( ! exists ) {
// insert into DB
}
}
I think the right function is:
function findTransactionByBill(billId){
var results = new promise(function(resolve, reject){
Transactions.find({billId : billId},function(err, transactions){
if(err) {
reject(err);
} else {
if (transactions.length === 0) {
reject('No any transaction');
} else {
//console.log(transactions);
resolve(transactions);
}
});
});
results.then(function(data){
console.log('Promise fullfilled: '+ data);
}, function(error){
console.log('Promise rejected: ' + error);
});
return results;
}
And then use it like this:
recordExists = findTranscationByBill(billId);
recordExists.then(function() {
// resolved, there are some transactions
}, function() {
// rejected. Error or no any transactions found
// may be you need to check reject result to act differently then no transactions and then error
});
I assume you are using mongodb native drive.
I think mongodb doesn't have promise built-in supported. So you have to promisify it by a little help from promise library. Please refer this if you want to use bluebird.
After promisifying, the code should looks like that (using bluebird):
Promise = require('bluebird');
// Promisify...
var _db = null;
var client = MongoClient.connectAsync('mongodb://localhost:27017/test')
.then(function(db) {
_db = db
return db.collection("myCollection").findOneAsync({ id: 'billId' })
})
.then(function(item) {
if (item)
_db.save(item);
})
.catch (err) {
// error handling
}
The above code is not perfect, because it introduced a global var, so the better version may be
Promise = require('bluebird');
// Promisify...
var client = MongoClient.connectAsync('mongodb://localhost:27017/test')
.then(function(db) {
return Promise.prop({
item: db.collection("myCollection").findOneAsync({ id: 'billId' },
db: db
})
})
.then(function(result) {
var item = result.item;
var db = result.db
if (item)
db.save(item);
})
.catch (err) {
// error handling
}
You need to check bluebird to know how to use it. Also they are many other promise libraries like q, when, but all are similar stuff.