I've been using Bluebird a lot recently on a HAPI API development. I've just run into my first real problem, that perhaps my understanding or naivety has me stumped.
The following code is an example of what I am facing:-
var Promise = require('bluebird'),
stuff = require('../stuff');
module.exports = {
getSomething: function(request, reply) {
var p = Promise.resolve();
p = p.then(function() {
return db.find() //etc. etc.
});
p = p.then(function(resultFromPromise) {
//problems begin here
var data = stuff.doSomeReallyLongAndBoringFunction(resultFromPromise);
return data;
});
p.then(function(data) {
//no data here.
});
};
};
I've commented where the problems usually begin. the stuff.doSomeReallyLongAndBoringFunction() returns an object (using more promises concidently) and it's this object I want to access, but //no data here always fires before data returns. stuff.doSomeReallyLongAndBoringFunction() continues to run regardless and completes successfully, but after the code goes async, I don't know how to promise that function's return value back.
Can anyone offer any guidance? Please accept my apologies for any naivety in the question!
Help as always, is appreciated
NB just for clarity, stuff.doSomeReallyLongAndBoringFunction() does not return a Promise itself. Although, I did try return new Promise(reject, resolve) { }); manual wrap. It is simply a function that uses promises itself (successfully) to get data.
Update 1
stuff.doSomeReallyLongAndBoringFunction() is too big to post directly, but it does something like this:-
var Promise = require('bluebird'),
rp = require('request-promise');
module.exports = {
doSomeReallyLongAndBoringFunction: function() {
var p = Promise.resolve();
p = p.then(function() {
return db.find() //etc. etc.
});
p.then(function() {
rp(options).then(function(response){
//get some data from remote location
}).then(function(dataFromService) {
//do some jiggery pokery with said data
var marshalledData = dataFromService;
db.something.create({
Field: 'something'
}).exec(function(err, saved) {
return marshalledData;
});
});
}).catch(function(err) {
});
};
};
Update 2
Thank you Justin for your help. Here is the actual code, perhaps this may help?
Promise.resolve()
.then(function() {
if(typeof utils.intTryParse(place) !== 'number') {
return foursquare.createPlaceFromFoursquare(sso, place, request, reply);
} else {
return { Place: { PlaceId: place }};
}
}).then(function(placeObj) {
console.log('Place set as', placeObj); //always returns undefined, despite function actually completing after async op...
});
If your doSomeReallyLongAndBoringFunction is really running asynchronously, then it doesn't make sense to run it the way you have setup.
Edit - Here's a simple explanation of the way your code looks to be running vs a refactored version. It's been simplified , so you'll need to fill in the relevant sections with your actual implementation.
var Promise = require('bluebird');
function myAsync() {
setTimeout(function(){
return 'done sleeping';
}, 2000);
};
//The way your code is running
Promise.resolve()
.then(function(){
return 'hello';
})
.then(function(done){
console.log(done);
return myAsync(); //your error is here
})
.then(function(done){
console.log(done);
});
//refactored
Promise.resolve()
.then(function(){
return 'hello';
})
.then(function(done){
console.log(done);
return new Promise(function(resolve) {
setTimeout(function(){
resolve('done sleeping');
}, 2000);
});
})
.then(function(done){
console.log(done);
});
just for clarity, stuff.doSomeReallyLongAndBoringFunction() does not return a Promise itself.
And that's your problem. As it does something asynchronous and you want to get its result, it should return a promise. In fact, that's the case for every asynchronous function, especially then callbacks! It should be something like
module.exports = {
doSomeReallyLongAndBoringFunction: function() {
return db.find()
// ^^^^^^
.then(function() {
return rp(options).then(function(response){
// ^^^^^^
//get some data from remote location
}).then(function(dataFromService) {
//do some jiggery pokery with said data
var marshalledData = dataFromService;
return db.something.create({
// ^^^^^^
Field: 'something'
}).execAsyc();
});
}).catch(function(err) {
});
}
};
Your getSomething method has the same issues, and should look like this:
var createPlace = Promise.promisify(foursquare.createPlaceFromFoursquare);
module.exports = {
getSomething: function(request) {
var p;
if (typeof utils.intTryParse(place) !== 'number')
p = createPlace(sso, place, request); // this returns a promise!
else
p = Promise.resolve({Place: {PlaceId: place}});
return p.then(function(placeObj) {
// ^^^^^^
console.log('Place set as', placeObj);
});
}
};
See also these generic rules for promise development.
doSomeReallyLongAndBoringFunction needs to look like this:
doSomeReallyLongAndBoringFunction: function(param) {
var resolver = Promise.defer();
/*
* do some asynchronous task and when you are finished
* in the callback, do this:
*/
resolver.resolve(resultFromAsyncTask);
/*
*
*
*/
return resolver.promise;
}
Related
I'm new to Node/Express and am trying to use Promises to executive successive API calls to Apple's CloudKit JS API.
I'm unclear on how to put the functions in sequence and pass their respective return values from one function to the next.
Here's what I have so far:
var CloudKit = require('./setup')
//----
var fetchUserRecord = function(emailConfirmationCode){
var query = { ... }
// Execute the query
CloudKit.publicDB.performQuery(query).then(function (response) {
if(response.hasErrors) {
return Promise.reject(response.errors[0])
}else if(response.records.length == 0){
return Promise.reject('Email activation code not found.')
}else{
return Promise.resolve(response.records[0])
}
})
}
//-----
var saveRecord = function(record){
// Update the record (recordChangeTag required to update)
var updatedRecord = { ... }
CloudKit.publicDB.saveRecords(updatedRecord).then(function(response) {
if(response.hasErrors) {
Promise.reject(response.errors[0])
}else{
Promise.resolve()
}
})
}
//----- Start the Promise Chain Here -----
exports.startActivation = function(emailConfirmationCode){
CloudKit.container.setUpAuth() //<-- This returns a promise
.then(fetchUserRecord) //<-- This is the 1st function above
.then(saveRecord(record)) //<-- This is the 2nd function above
Promise.resolve('Success!')
.catch(function(error){
Promise.reject(error)
})
}
I get an error near the end: .then(saveRecord(record)) and it says record isn't defined. I thought it would somehow get returned from the prior promise.
It seems like this should be simpler than I'm making it, but I'm rather confused. How do I get multiple Promises to chain together like this when each has different resolve/reject outcomes?
There are few issues in the code.
First: you have to pass function to .then() but you actually passes result of function invocation:
.then(saveRecord(record))
Besides saveRecord(record) technically may return a function so it's possible to have such a statement valid it does not seem your case. So you need just
.then(saveRecord)
Another issue is returning nothing from inside saveRecord and fetchUserRecord function as well.
And finally you don't need to return wrappers Promise.resolve from inside .then: you may return just transformed data and it will be passed forward through chaining.
var CloudKit = require('./setup')
//----
var fetchUserRecord = function(emailConfirmationCode){
var query = { ... }
// Execute the query
return CloudKit.publicDB.performQuery(query).then(function (response) {
if(response.hasErrors) {
return Promise.reject(response.errors[0]);
}else if(response.records.length == 0){
return Promise.reject('Email activation code not found.');
}else{
return response.records[0];
}
})
}
//-----
var saveRecord = function(record){
// Update the record (recordChangeTag required to update)
var updatedRecord = { ... }
return CloudKit.publicDB.saveRecords(updatedRecord).then(function(response) {
if(response.hasErrors) {
return Promise.reject(response.errors[0]);
}else{
return Promise.resolve();
}
})
}
//----- Start the Promise Chain Here -----
exports.startActivation = function(emailConfirmationCode){
return CloudKit.container.setUpAuth() //<-- This returns a promise
.then(fetchUserRecord) //<-- This is the 1st function above
.then(saveRecord) //<-- This is the 2nd function above
.catch(function(error){});
}
Don't forget returning transformed data or new promise. Otherwise undefined will be returned to next chained functions.
Since #skyboyer helped me figure out what was going on, I'll mark their answer as the correct one.
I had to tweak things a little since I needed to pass the returned values to subsequent functions in my promise chain. Here's where I ended up:
exports.startActivation = function(emailConfirmationCode){
return new Promise((resolve, reject) => {
CloudKit.container.setUpAuth()
.then(() => {
return fetchUserRecord(emailConfirmationCode)
})
.then((record) => {
resolve(saveRecord(record))
}).catch(function(error){
reject(error)
})
})
}
I have a conditional statement in which I need to perform one of two operations, then continue after whichever operation has resolved. So my code currently looks as follows:
if (shoud_do_thing_a) { //should_do_thing_a is just a variable that determines which function to call. it is not a promise
do_thing_a()
} else {
do_thing_b()
}
// more code
The issue is that both do_thing_a and do_thing_b return promises, and I can't move on until whichever gets executed has resolved. The best way I've come up with to solve this is like this:
var more_code = function () {
// more code
}
if (shoud_do_thing_a) {
do_thing_a().then(more_code)
} else {
do_thing_b().then(more_code)
}
I don't like this structure. It's difficult to follow because you need to jump around to find where more_code is defined (imagine I have this type of control flow in several locations), rather than simply being able to continue reading.
Is there a better way to deal with this type of thing in javascript?
If you can use async/await
async function someFunc() {
var more_code = function () {
// more code
}
if (shoud_do_thing_a) {
await do_thing_a()
} else {
await do_thing_b()
}
more_code()
}
Or if you can't, use then():
var more_code = function () {
// more code
}
var do_thing;
if (shoud_do_thing_a) {
do_thing = do_thing_a()
} else {
do_thing = do_thing_b()
}
do_thing.then(more_code)
If you're stuck with raw Promises and can't use async/await (You usually should have no trouble, what with babel/typescript etc), the following is a bit more elegant than storing the promise in a variable:
function something() {
return Promise.resolve()
.then(() => {
if (should_do_thing_a) {
return do_thing_a();
}
else if (should_do_thing_b) {
return do_thing_b();
}
})
.then(some_more_code);
}
Note that when you start working with Promises, your functions should always return a Promise that other functions can work with. Leaving an asynchronous action without any way to handle it means bad things, especially when it comes to error handling.
In a more general sense, it means that when you use Promises, more of your code is "uplifted" into being executed and returned as Promises.
How I want to improve on other answers:
keep it clean and simple
no unneeded variables
return promise asap
in js we use camelCase
put it in a function and name that function to keep it readable
let then execute moreCode so it's called after the thing is done.
function doTheThing () {
if (shouldDoA) return doThingA()
else return doThingB()
}
doTheThing().then(moreCode)
Simple working example:
The scope it's defined in must be async.
const createUser = async (data) => {
if (await isUsernameTaken(username)) { return 'username-taken' }
}
The isUsernameTaken func:
const isUsernameTaken = async (username) => {
const request = await API.SomeRequest
return !request.isEmpty
}
Save the promise and add the then after the if statement:
var promise;
if (shoud_do_thing_a) {
promise = do_thing_a();
} else {
promise = do_thing_b();
}
promise.then(more_code);
var promise = shoud_do_thing_a? do_thing_a: do_thing_b
promise().then(function () {
// more code
})
Similar to other answers here, but you can self execute the async and clean up the condition a bit.
(async () => {
const should_do_thing_a = true
const do_thing_a = function() {
return new Promise(function(resolve, reject) {
resolve('a')
})
}
const do_thing_b = function() {
return new Promise(function(resolve, reject) {
resolve('b')
})
}
const result = (should_do_thing_a) ? await do_thing_a() : await do_thing_b()
console.log(result)
})()
The way I would do it would be to put the if check into another function that returns a promise. The promise gets resolved with the resolve of the other function calls in the if-else statement.
Example:
function performCheck(condition) {
var defer = $q.defer();
if (condition) {
doThingA().then(function(response) {
defer.resolve(response);
});
} else {
doThingB().then(function(response) {
defer.resolve(response)
});
}
return defer.promise;
}
performCheck(condition).then(function(response) {
//Do more code.
});
In my opinion, I would prefer this method because this function can now be used in multiple places where you have a check on the condition, reducing code duplication, and it is easier to follow.
You could reduce this down further with
function performCheck(condition) {
var defer = $q.defer();
var doThisThing = condition ? doThingA : doThingB;
doThisThing().then(function (response) {
defer.resolve(response);
});
return defer.promise;
}
performCheck(condition).then(function(response) {
//Do more code.
});
You can use async/await
async function fn() {
let p, result;
if (shoud_do_thing_a) {
p = await do_thing_a()
} else {
p = await do_thing_b()
}
if (p) {
result = more_code();
}
return result
}
more_code = miFunc() => return new Promise((resolve, reject) => ... });
Solution 1
const waitFor = should_do_thing_a ? do_thing_a() : do_thing_b();
waitFor.then(...).catch(...)
Solution 2
let waitFor = Promise.resolve();
if (do_thing_a) {
waitFor = do_thing_a();
} else {
waitFor = do_thing_b();
}
waitFor.then(...).catch(...);
I have a small problem, this script works perfectly, with one problem, the "runTenant" method is not returning a promise (that needs resolving from "all()".
This code:
Promise.resolve(runTenant(latest)).then(function() {
end();
});
Calls this code:
function runTenant(cb) {
return new Promise(function() {
//global var
if (!Tenant) {
loadCoreModels();
Tenant = bookshelf.core.bs.model('Tenant');
}
new Tenant().fetchAll()
.then(function(tenants) {
if (tenants.models.length == 0) {
return;
} else {
async.eachSeries(tenants.models, function(tenant, next) {
var account = tenant.attributes;
Promise.resolve(db_tenant.config(account)).then(function(knex_tenant_config) {
if (knex_tenant_config) {
db_tenant.invalidateRequireCacheForFile('knex');
var knex_tenant = require('knex')(knex_tenant_config);
var knex_pending = cb(knex_tenant);
Promise.resolve(knex_pending).then(function() {
next(null, null);
});
} else {
next(null, null);
}
});
});
};
});
});
}
The code from runTenant is working correctly however it stalls and does not proceed to "end()" because the promise from "runTenant(latest)" isn't being resolved.
As if it weren't apparent, I am horrible at promises. Still working on getting my head around them.
Many thanks for any help/direction!
You should not use the Promise constructor at all here (and basically, not anywhere else either), even if you made it work it would be an antipattern. You've never resolved that promise - notice that the resolve argument to the Promise constructor callback is a very different function than Promise.resolve.
And you should not use the async library if you have a powerful promise library like Bluebird at hand.
As if it weren't apparent, I am horrible at promises.
Maybe you'll want to have a look at my rules of thumb for writing promise functions.
Here's what your function should look like:
function runTenant(cb) {
//global var
if (!Tenant) {
loadCoreModels();
Tenant = bookshelf.core.bs.model('Tenant');
}
return new Tenant().fetchAll().then(function(tenants) {
// if (tenants.models.length == 0) {
// return;
// } else
// In case there are no models, the loop iterates zero times, which makes no difference
return Promise.each(tenants.models, function(tenant) {
var account = tenant.attributes;
return db_tenant.config(account).then(function(knex_tenant_config) {
if (knex_tenant_config) {
db_tenant.invalidateRequireCacheForFile('knex');
var knex_tenant = require('knex')(knex_tenant_config);
return cb(knex_tenant); // can return a promise
}
});
});
});
}
Your promise in runTenant function is never resolved. You must call resolve or reject function to resolve promise:
function runTenant() {
return new Promise(function(resolve, reject) {
// somewhere in your code
if (err) {
reject(err);
} else {
resolve();
}
});
});
And you shouldn't pass cb in runTenant function, use promises chain:
runTenant()
.then(latest)
.then(end)
.catch(function(err) {
console.log(err);
});
You need to return all the nested promises. I can't run this code, so this isn't a drop it fix. But hopefully, it helps you understand what is missing.
function runTenant(cb) {
//global var
if (!Tenant) {
loadCoreModels();
Tenant = bookshelf.core.bs.model('Tenant');
}
return new Tenant().fetchAll() //added return
.then(function (tenants) {
if (tenants.models.length == 0) {
return;
} else {
var promises = []; //got to collect the promises
tenants.models.each(function (tenant, next) {
var account = tenant.attributes;
var promise = Promise.resolve(db_tenant.config(account)).then(function (knex_tenant_config) {
if (knex_tenant_config) {
db_tenant.invalidateRequireCacheForFile('knex');
var knex_tenant = require('knex')(knex_tenant_config);
var knex_pending = cb(knex_tenant);
return knex_pending; //return value that you want the whole chain to resolve to
}
});
promises.push(promise); //add promise to collection
});
return Promise.all(promises); //make promise from all promises
}
});
}
I've just started using js modules with node, require and browserify and trying get code to work, that was originally in a single script.
I'd also like to start using promisejs but am unsure how to apply it in this situation.
All the require browserify parts seem to work so I'll leave that out all the none relevant bits for simplicity.
In the one module I have something like this
module.exports.api = function(){
var getCurrentProcessInstanceTask = function getCurrentProcessInstanceTask(options) {
if (!currentProcess || !currentProcess.id) {
throw new Error("no currentProcess is set, cannot get active task");
return;
}
var processInstanceId = currentProcess.id;
jQuery.get(hostUrl + "service/runtime/tasks", {
processInstanceId: processInstanceId
})
.done(function(data) {
console.log("response: " + JSON.stringify(data, null, 2));
currentProcess.tasks = data.data;
if (options && options.callback) {
options.callback(data.data);
}
});
}
return {
getCurrentProcessInstanceTask: getCurrentProcessInstanceTask
}
}
Then in the other module I have something like this
var Api = require('./api');
module.exports.view = function(){
var api = Api();
var setupEmbeddedView = function setupEmbeddedView(url, tmpl) {
tmpls.renderExtTemplate({
name: tmpl,
selector: targetDiv,
data: {
url: url,
width: iframeTargetDiv.width(),
height: iframeTargetDiv.height()
},
callback: function() {
jQuery('#taskFormFrame').load(function(e) {
console.log("taskFormFrame load fired!");
});
}
});
},
showCurrentTaskForm = function showCurrentTaskForm() {
console.log("mark");
api.getCurrentProcessInstanceTask({
callback: function(tasks) {
setupEmbeddedView(getTaskFormUrl(tasks), 'showTaskForm');
}
});
}
return {
showCurrentTaskForm: showCurrentTaskForm
}
}
calling showCurrentTaskForm in another module where view is required results in the api.getCurrentProcessInstanceTask part being executed but setupEmbeddedView never seems to get called.
I'm very confused about why maybe someone can explain.
Also I'd like an example of how I would apply promisejs in this particular case instead of using callbacks to chain functions
Current version of jQuery does not fulfill the Promises/A+ specification. I would recommend the use of a separate promise library, such as bluebird.
Each function that runs an asynchronous block of code should return a promise.
Looking at your code, the getCurrentProcessInstanceTask and thus it should return a promise.
Since you are using jQuery.get() which returns a jQuery promise, you will have to let bluebird assimilate that promise to create a proper promise chain.
var getCurrentProcessInstanceTask = function getCurrentProcessInstanceTask() {
if (!currentProcess || !currentProcess.id) {
return Promise.reject(new Error("no currentProcess is set, cannot get active task"))
}
var processInstanceId = currentProcess.id;
return Promise.resolve(jQuery.get(hostUrl + "service/runtime/tasks", {processInstanceId: processInstanceId})
.then(function(data){
console.log("response: " + JSON.stringify(data, null, 2));
currentProcess.tasks = data.data;
return data.data;
});
);
}
To run it sequentially, just modify the showCurrentTaskForm function to call the api function and .then() the returned promise.
showCurrentTaskForm = function showCurrentTaskForm() {
console.log("mark");
api.getCurrentProcessInstanceTask()
.then(function(result){
setupEmbeddedView(result, 'showTaskForm');
})
.catch(function(e){
//handle the error
});
}
I'm using Node.js and Bluebird to create some fairly complicated logic involving uncompressing a structured file, parsing JSON, creating and making changes to several MongoDB documents, and writing related files in multiple locations. I also have fairly complicated error handling for all of this depending on the state of the system when an error occurs.
I am having difficulty thinking of a good way to manage dependencies through the flow of promises.
My existing code basically looks like this:
var doStuff = function () {
var dependency1 = null;
var dependency2 = null;
promise1()
.then(function (value) {
dependency1 = value;
return promise2()
.then(function (value) {
dependency2 = value;
return promise3(dependency1)
.then(successFunction);
});
})
.catch(function (err) {
cleanupDependingOnSystemState(err, dependency1, dependency2);
});
};
Note that dependency1 isn't needed until promise3, and that the error handler needs to know about the dependencies.
To me this seems like spaghetti code (and my actual code is far worse with a lot of parallel control flow). I've also read that returning another promise inside of a .then callback is an antipattern. Is there a better/cleaner way of accomplishing what I'm trying to do?
I find both answers currently provided nice but clumsy. They're both good but contain overhead I don't think you need to have. If you instead use promises as proxies you get a lot of things for free.
var doStuff = function () {
var p1 = promise1();
var p2 = p1.then(promise2);
var p3 = p1.then(promise3); // if you actually need to wait for p2 here, do.
return Promise.all([p1, p2, p3]).catch(function(err){
// clean up based on err and state, can unwrap promises here
});
};
Please do not use successFunction and such it is an anti-pattern and loses information.
If you feel like you have to use successFunction you can write:
var doStuff = function () {
var p1 = promise1();
var p2 = p1.then(promise2);
var p3 = p1.then(promise3); // if you actually need to wait for p2 here, do.
Promise.join(p1, p2, p3, successFunction).catch(function(err){
// clean up based on err and state, can unwrap promises here
});
};
However, it is infinitely worse since it won't let the consumer handle errors they may be able to handle.
This question might be more appropriate for code review but here is how I'd approach it given this example:
var doStuff = function () {
// Set up your promises based on their dependencies. In your example
// promise2 does not use dependency1 so I left them unrelated.
var dep1Promise = promise1();
var dep2Promise = promise2();
var dep3Promise = dependency1Promise.then(function(value){
return promise3(value);
});
// Wait for all the promises the either succeed or error.
allResolved([dep1Promise, dep2Promise, dep3Promise])
.spread(function(dep1, dep2, dep3){
var err = dep1.error || dep2.error || dep3.error;
if (err){
// If any errored, call the function you prescribed
cleanupDependingOnSystemState(err, dep1.value, dep2.value);
} else {
// Call the success handler.
successFunction(dep3.value);
}
};
// Promise.all by default just fails on the first error, but since
// you want to pass any partial results to cleanupDependingOnSystemState,
// I added this helper.
function allResolved(promises){
return Promise.all(promises.map(function(promise){
return promise.then(function(value){
return {value: value};
}, function(err){
return {error: err};
});
});
}
The use of allResolved is only because of your callback specifics, if you had a more general error handler, you could simply resolve using Promise.all directly, or even:
var doStuff = function () {
// Set up your promises based on their dependencies. In your example
// promise2 does not use dependency1 so I left them unrelated.
var dep1Promise = promise1();
var dep2Promise = promise2();
var dep3Promise = dependency1Promise.then(function(value){
return promise3(value);
});
dep3Promise.then(successFunction, cleanupDependingOnSystemState);
};
It is certainly not an antipattern to return promises within thens, flattening nested promises is a feature of the promise spec.
Here's a possible rewrite, though I'm not sure it's cleaner:
var doStuff = function () {
promise1()
.then(function (value1) {
return promise2()
.then(function (value2) {
return promise3(value1)
.then(successFunction)
.finally(function() {
cleanup(null, value1, value2);
});
})
.finally(function() {
cleanup(null, value1, null);
});
})
.finally(function () {
cleanup(null, null, null);
});
};
Or another option, with atomic cleanup functions:
var doStuff = function () {
promise1()
.then(function (value1) {
return promise2()
.then(function (value2) {
return promise3(value1)
.then(successFunction)
.finally(function() {
cleanup3(value2);
});
})
.finally(function() {
cleanup2(value1);
});
})
.finally(function (err) {
cleanup1(err);
});
};
Really, I feel like there's not much you can do to clean this up. Event with vanilla try/catches, the best possible pattern is pretty similar to these.