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).
Related
I have a problem with making Promise working as expected. I need to do following thing:
I get file names from stdout, split them into line and copy them. When copy operation is finished i want to start other operations and here is my problem.
I've created a copy function inside Promise, in case of error i reject it immediately, if thereare no errors i resolve it after copy in loop is finished but for some reason the function inside then() gets executed before copy operation is done
var lines = stdout.split(/\r?\n/);
copyUpdatedFiles(lines).then(
function() {
console.log('this one should be executed after copy operation');
}
);
function copyUpdatedFiles(lines) {
return new Promise(function(resolve, reject) {
for (var i = 0; i < linesLength; i++) {
fs.copy(lines[i], target, function(err) {
if (err) {
reject();
}
});
}
resolve();
});
}
Please help cause i'm clearly missing something.
It gets resolved as soon as you call resolve, which you're doing after starting the copies but before they finish. You have to wait for the last callback before you resolve. That means keeping track of how many you've see, see the *** comments:
function copyUpdatedFiles(lines) {
return new Promise(function(resolve, reject) {
var callbacks = 0; // ***
for (var i = 0; i < linesLength; i++) {
fs.copy(lines[i], target, function(err) {
if (err) {
reject();
} else { // ***
if (++callbacks == lines.length) { // ***
resolve(); // ***
} // ***
} // ***
});
}
});
}
Alternately, there are a couple of libraries out there that promise-ify NodeJS-style callbacks so you can use standard promise composition techniques like Promise.all. If you were using one of those, you'd just do something this:
function copyUpdatedFiles(lines) {
return Promise.all(
// CONCEPTUAL, semantics will depend on the promise wrapper lib
lines.map(line => thePromiseWrapper(fs.copy, line, target))
);
}
Side note: Your loop condition refers to a variable linesLength that isn't defined anywhere in your code. It should be lines.length.
You don't wait for the copy to success before resolving the promise, after the for, all fs.copy has been put in the call stack, but they didn't complete.
You can either use a counter inside the callback of fs.copy and call resolve once every callback has been called, or use async.
var async = require('async');
var lines = stdout.split(/\r?\n/);
copyUpdatedFiles(lines).then(
function() {
console.log('this one should be executed after copy operation');
}
);
function copyUpdatedFiles(lines) {
return new Promise(function(resolve, reject) {
async.map(lines, (line, callback) => {
fs.copy(line, target, (err) => {
callback(err);
});
},
(err) => {
if(err) {
reject();
} else {
resolve();
}
});
});
}
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);
The meteor project I'm working on uploads files when certain conditions are satisfied. Regardless of whether or not the files are uploaded, a Meteor.call has to be made once the if statements have completed. Because of the conditionals, when I use callbacks it results in a lot of duplicate code. As it is written below, I expect the Meteor.call could be executed before the uploadFile callbacks get executed which would be a problem.
var data = {
name: "..."
//...
}
if(condition){
uploadFile(parameters, function(error,result){
if(err) handleError(err);
else data.url1 = result.secure_url;
}
if(condition2){
uploadFile(parameters, function(error,result){
if(err) handleError(err);
else data.url2 = result.secure_url;
}
/* This Meteor.call needs to wait until both if statements above
have completed */
Meteor.call('insertData', data, function(error,result){
//...
}
you could remove the conditionals and instead implement Javascript Promises they're fun and fancy plus they eliminate callback hell and provide a readable top down format:
http://jsfiddle.net/4v29u4do/1/
This fiddle shows how you could use promises to wait for async callbacks instead of conditional if statements
With promises you can return a new promise as the callback from a promise, and keep going and going and the callback will get passed to the new .then until you're done if there is any errors along the way, it skips right to the .catch.
function uploadFile(file) {
return new Promise(function(resolve, reject) {
// Simulate ASYNC Call for Uploading File
setTimeout(function() {
console.log(file + ' Uploaded Successfully!');
return resolve(file);
// if (err) {
// return reject(err);
// }
}, 3000);
});
}
var data = {};
uploadFile("filename1")
.then(function(cb) {
data.url1 = cb;
return uploadFile("filename2");
})
.then(function(cb) {
data.url2 = cb;
return uploadFile("filename5");
})
.then(function(cb) {
data.url3 = cb;
console.log(data);
//all done with callbacks
// Meteor.call("");
})
.catch(function(err) {
// One of the uploads failed log the err;
});
I'm new to RxJS and FRP in general. I had the idea of converting an existing promise chain in my ExpressJS application to be an observable for practice. I am aware that this probably isn't the best example but maybe someone can help shed some light.
What I'm trying to do:
I have two promises - prom1 and prom2
I want prom1 to run before prom2
If prom1 sends a reject(err), I want to cancel prom2 before it starts.
I want the error message prom1 returns to be available to the onError method on the observer.
var prom1 = new Promise(function(resolve, reject) {
if (true) {
reject('reason');
}
resolve(true);
});
var prom2 = new Promise(function(resolve, reject) {
resolve(true);
});
// What do I do here? This is what I've tried so far...
var source1 = Rx.Observable.fromPromise(prom1);
var source2 = source1.flatMap(Rx.Observable.fromPromise(prom2));
var subscription = source2.subscribe(
function (result) { console.log('Next: ' + result); },
// I want my error 'reason' to be made available here
function (err) { console.log('Error: ' + err); },
function () { console.log('Completed'); });
If I understood what you are trying to do - you need to create two deferred observables from functions that return promises and concat them:
var shouldFail = false;
function action1() {
return new Promise(function (resolve, reject) {
console.log('start action1');
if (shouldFail) {
reject('reason');
}
resolve(true);
});
}
function action2() {
return new Promise(function (resolve, reject) {
console.log('start action2');
resolve(true);
});
}
var source1 = Rx.Observable.defer(action1);
var source2 = Rx.Observable.defer(action2);
var combination = Rx.Observable.concat(source1, source2);
var logObserver = Rx.Observer.create(
function (result) {
console.log('Next: ' + result);
},
function (err) {
console.log('Error: ' + err);
},
function () {
console.log('Completed');
});
then for normal case:
combination.subscribe(logObserver);
// start action1
// Next: true
// start action2
// Next: true
// Completed
And case where fisrt promise fails:
shouldFail = true;
combination.subscribe(logObserver);
// start action1
// Error: reason
http://jsfiddle.net/cL37tgva/
flatMap turns an Observable of Observables into an Observable. It's used in many examples with Promises because often you have an observable and in the map function you want to create a promise for each "item" the observable emmits. Because every fromPromise call creates a new Observable, that makes it an "observable of observables". flatMap reduces that to a "flat" observable.
In your example you do something different, you turn a single promise into an observable and want to chain it with another observable (also created form a single promise). Concat does what you are looking for, it chains two observables together.
The error case will work as you would expect.
Observable.forkJoin works great here receiving array of other Observables.
Rx.Observable.forkJoin([this.http.get('http://jsonplaceholder.typicode.com/posts'), this.http.get('http://jsonplaceholder.typicode.com/albums')]).subscribe((data) => {
console.log(data);
});
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.