Let's say I have some code that looks like this:
var doSomething = function(parameter){
//send some data to the other function
return when.promise(function(resolveCallback, rejectCallback) {
var other = doAnotherThing(parameter);
//how do I check and make sure that other has resolved
//go out and get more information after the above resolves and display
});
};
var doAnotherThing = function(paramers){
return when.promise(function(resolveCallback, rejectCallback) {
//go to a url and grab some data, then resolve it
var s = "some data I got from the url";
resolveCallback({
data: s
});
});
};
How do I ensure that var other has completely resolved before finishing and resolving the first doSomething() function? I'm still wrapping my head around Nodes Async characteristic
I really didn't know how else to explain this, so I hope this makes sense! Any help is greatly appreciated
EDIT: In this example, I am deleting things from an external resource, then when that is done, going out the external resource and grabbing a fresh list of the items.
UPDATED CODE
var doSomething = function(parameter){
//send some data to the other function
doAnotherThing(parameter).then(function(){
//now we can go out and retrieve the information
});
};
var doAnotherThing = function(paramers){
return when.promise(function(resolveCallback, rejectCallback) {
//go to a url and grab some data, then resolve it
var s = "some data I got from the url";
resolveCallback({
data: s
});
});
};
The return of doAnotherThing appears to be a promise. You can simply chain a then and put your callback to utilize other. then also already returns a promise. You can return that instead.
// Do stuff
function doSomething(){
return doAnotherThing(parameter).then(function(other){
// Do more stuff
return other
});
}
// Usage
doSomething().then(function(other){
// other
});
Below is how to accomplish what you're trying to do with bluebird.
You can use Promise.resolve() and Promise.reject() within any function to return data in a Promise that can be used directly in your promise chain. Essentially, by returning with these methods wrapping your result data, you can make any function usable within a Promise chain.
var Promise = require('bluebird');
var doSomething = function(parameter) {
// Call our Promise returning function
return doAnotherThing()
.then(function(value) {
// Handle value returned by a successful doAnotherThing call
})
.catch(function(err) {
// if doAnotherThing() had a Promise.reject() in it
// then you would handle whatever is returned by it here
});
}
function doAnotherThing(parameter) {
var s = 'some data I got from the url';
// Return s wrapped in a Promise
return Promise.resolve(s);
}
You can use the async module and its waterfall method to chain the functions together:
var async = require('async');
async.waterfall([
function(parameter, callback) {
doSomething(parameter, function(err, other) {
if (err) throw err;
callback(null, other); // callback with null error and `other` object
});
},
function(other, callback) { // pass `other` into next function in chain
doAnotherThing(other, function(err, result) {
if (err) throw err;
callback(null, result);
})
}
], function(err, result) {
if (err) return next(err);
res.send(result); // send the result when the chain completes
});
Makes it a little easier to wrap your head around the series of promises, in my opinion. See the documentation for explanation.
Related
I've spent so much time trying to find the answer to this on here and come up with nothing. Hoping someone can enlighten me..
I have code which is making an async call to a database and returning data in a callback function (in my case I'm using MongoClient, which returns a Promise). However, I can't work out how to use the resulting data to actually set function-level variables - whenever I try to do it the resulting value that I log is either undefined or a pending Promise object.
There's lots of posts on this subject but I can't find any methods that work when I try to apply them. Any and all help gratefully received!
function lookupOneDbEntry(key, value) {
var responseData = "initial data"
// search for the entry in the database
db.collection("my_collection").findOne({key: value}, function(err, result) {
if (err) {
//if database throws an error
responseData = "db error";
}
else {
// if the entry is found, return the data
responseData = result;
}
});
return responseData;
}
EDIT: I am aware of other posts on this (like this one here and, while exhaustive documentation is useful to an extent, I;m having trouble using this information practically in a real-life implementation like the one above. Hence my question here.
Async calls happens outside of the call stack that you are on. you can't return it onto the current stack.
So we use the promises to hook into the results of our call.
function lookupOneDbEntry(key, value) {
return new Promise(function (resolve, reject) {
// search for the entry in the database
db.collection("my_collection").findOne({key: value}, function(err, result) {
if (err) {
//if database throws an error
reject(err);
}
else {
// if the entry is found, return the data
resolve(result);
}
});
});
}
lockupOneDbEntry('myKey', 'myValue').then(function (result) {
console.log('result', result);
}, function (err) {
console.log("error!", err);
});
After a long while of experimenting I've finally managed to do it - I didn't need any fancy callbacks or additional Promises in the end, I just removed the optional callback in the database request and instead processed the returned promise separately.
function lookupOneDbEntry(key, value) {
var responseData = "initial data";
var solution = db.collection("accounting_module").findOne({key: value});
solution.then(function (result) {
// validation of result here
responseData = result;
});
return responseData;
}
I have a Node.js function to fetch some value from DB table
var GetPoints = function(ibmdb, dbConnection, msisdn) {
ibmdb.open(dbConnection, function(err, conn) {
if (err) {
//Error
} else {
conn.query('SELECT id,msisdn,points FROM t_points WHERE msisdn =' + msisdn, function(err, data) {
console.log(err);
if (!err) {
conn.close(function(err) {
if (!err) {}
});
consele.log(data);
//return data[0].POINTS;
} else {
//Error
}
});
}
console.log("points" + points);
});
}
I want to know how I can access the data object when I call this function from outside
var data = GetPoints(ibmdb, dbConnection, msisdn);
The value is coming correctly when I do a console.log
You can't return the value from an async function directly. Promises are generally used this situation. You return a promise which can later be called .then upon to retrieve the said value.
var Promise = require('bluebird');
var GetPoints = function(ibmdb, dbConnection, msisdn) {
// return a Promise
return new Promise(function(resolve){
ibmdb.open(dbConnection, function(err, conn) {
if(err) throw err; // throw the error for it to be caught
…
conn.query('SELECT …', function(err, data) {
if(err) throw err;
…
consele.log(data);
//return data[0].POINTS;
resolve(data);
}); }); }); }
GetPoints().then(function(data){
// do something with data
}).catch(function(err){
// handle err
});
Additionally, Bluebird has a promisify function that turns an async function (that takes a callback) into a function that returns a Promise. It makes the above code much simpler:
Note: Although I was reluctant because if you're using MySQL with which the promisification could be a little tricky: 1, 2. But For now I've added .promisifyAll where it might seem redundant as it will likely be executed more than once, but hopefully bluebird's promisification is smart enough to handle this. Nonetheless if you manage to promisify more efficiently you can just remove the redundant promisifyAll and just use X.yyyAsync methods as described:
function GetConnection(ibmdb, dbConnection, msisdn){
Promise.promisifyAll(ibmdb);
return ibmdb.openAsync();
}
function getData(conn){
Promise.promisifyAll(conn);
return conn.queryAsync('SELECT …');
}
GetConnection()
.then(getData)
.then(function(data){
// do something with data
})
The callback function you gave after the SQL query gets executed asynchronously. Instead of trying to get that data outside the function, you should try to perform whatever you need to do inside. Keep in mind you can create another function and call it with the data to continue your work.
it is a common pattern that we cascade across a list of sources of data with the first success breaking the chain like this:
var data = getData1();
if (!data) data = getData2();
if (!data) data = getData3();
et cetera. if the getDataN() functions are asynchronous, however, it leads us to 'callback hell':
var data;
getData1(function() {
getData2(function () {
getData3(function () { alert('not found'); })
})
});
where the implementations may look something like:
function getData1(callback) {
$.ajax({
url: '/my/url/1/',
success: function(ret) { data = ret },
error: callback
});
}
...with promises I would expect to write something like this:
$.when(getData1())
.then(function (x) { data = x; })
.fail(function () { return getData2(); })
.then(function (x) { data = x; })
.fail(function () { return getData3(); })
.then(function (x) { data = x; });
where the second .then actually refers to the return value of the first .fail, which is itself a promise, and which I understood was chained in as the input to the succeeding chain step.
clearly I'm wrong but what is the correct way to write this?
In most promise libs, you could chain .fail() or .catch() as in #mido22's answer, but jQuery's .fail() doesn't "handle" an error as such. It is guaranteed always to pass on the input promise (with unaltered state), which would not allow the required "break" of the cascade if/when success happens.
The only jQuery Promise method that can return a promise with a different state (or different value/reason) is .then().
Therefore you could write a chain which continues on error by specifying the next step as a then's error handler at each stage.
function getDataUntilAsyncSuccess() {
return $.Deferred().reject()
.then(null, getData1)
.then(null, getData2)
.then(null, getData3);
}
//The nulls ensure that success at any stage will pass straight through to the first non-null success handler.
getDataUntilAsyncSuccess().then(function (x) {
//"success" data is available here as `x`
}, function (err) {
console.log('not found');
});
But in practice, you might more typically create an array of functions or data objects which are invoked in turn with the help of Array method .reduce().
For example :
var fns = [
getData1,
getData2,
getData3,
getData4,
getData5
];
function getDataUntilAsyncSuccess(data) {
return data.reduce(function(promise, fn) {
return promise.then(null, fn);
}, $.Deferred().reject());// a rejected promise to get the chain started
}
getDataUntilAsyncSuccess(fns).then(function (x) {
//"success" data is available here as `x`
}, function (err) {
console.log('not found');
});
Or, as is probably a better solution here :
var urls = [
'/path/1/',
'/path/2/',
'/path/3/',
'/path/4/',
'/path/5/'
];
function getDataUntilAsyncSuccess(data) {
return data.reduce(function(promise, url) {
return promise.then(null, function() {
return getData(url);// call a generalised `getData()` function that accepts a URL.
});
}, $.Deferred().reject());// a rejected promise to get the chain started
}
getDataUntilAsyncSuccess(urls).then(function (x) {
//"success" data is available here as `x`
}, function (err) {
console.log('not found');
});
As a beginner, stumbling across the same problem, I just realized how much simpler this has become with async and await:
The synchronous pattern
var data = getData1();
if (!data) data = getData2();
if (!data) data = getData3();
can now easily be applied to asynchronous code:
let data = await getData1();
if (!data) data = await getData2();
if (!data) data = await getData3();
Just remember to add an async to the function that this code is used in.
I'm trying to get my head around promises in JavaScript. I feel like I know what a promise is. However, I don't understand how to use them. In an attempt to learn, I decided to query a database from Node.js. In my code, I have one JavaScript file called test.js. Test.js looks like this:
Test.js
'use strict';
module.exports = function (app) {
var customerService = require('customer.js')(app);
var getCustomerTest = function() {
customerService.getCustomer(1).then(
function (customer) { console.log(customer); },
function (error) { console.log(error); }
);
};
};
Customer.js
'use strict';
module.exports = function(app) {
var _ = require('lodash');
var db = require('db');
return {
getCustomer: function(customerID) {
try {
console.log('querying the database...');
var database = db.connect(CONNECTION_STRING);
database.query('select * from customers where [ID]="' + customerID + '", function(error, result, response) {
if (error) {
// trigger promise error
} else {
// This throws an exception because displayMessage can't be found.
this.displayMessage('Success');
// trigger promise success
}
});
} catch (ex) {
// trigger promise error
}
},
displayMessage: function(message) {
console.log(new Date() + ' - ' + message);
}
};
};
I'm struggling trying to setup the promise in getCustomer. Especially since the call to the database call happens asynchronously. I feel like my call to customerService.getCustomer is the correct approach. However, once inside of getCustomer, I have two issues:
How do I setup / return my promise?
Why can't I call displayMessage after the database query is done? How do I do this?
Thank you JavaScript whiz!
"Why can't I call displayMessage after the database query is done"
You are getting the error because this is not referencing the object that contains getCustomer and displayMessage. This is because you are in a callback function and the context has changed.
You need to save a reference to the correct context and use that to access displayMessage
getCustomer: function(customerID) {
//saving the context
var that = this;
try {
...
database.query('select * from customers where [ID]="' + customerID + '", function(error, result, response) {
...
// Now use it here to call displayMessage
that.displayMessage('Success');
...
}
});
} catch (ex) {
...
}
},
"How do I setup / return my promise?"
You will need a promise library (unless you plan to make your own). For this I will show the use of the q library
There are a couple ways of doing this, but I will show the use of deferreds.
The basic process is:
Create a deferred object in a function that will call an async function/method.
Return the promise object
Set the appropriate callbacks for then/fail/fin etc on the promise object.
In the callback of the async function resolve or reject the deferred, passing any arguments that will be needed in the callbacks.
The appropriate callbacks that were set in step 2 will then be called in order.
Code
var Q = require("q");
...
getCustomer:{
var deferred = Q.defer(),
database = db.connect(CONNECTION_STRING);
database.query("some query", function(error, result, response) {
if(error){
//Anything passed will be passed to any fail callbacks
deferred.reject(error);
} else {
//Anything passed will be passed to any success callbacks
deferred.resolve(response);
}
});
//Return the promise
return deferred.promise;
}
...
customerService.getCustomer(1).then(function (customer) {
console.log(customer);
}).fail(function (error) {
console.log(error);
}).done();
The q library has quite a few helpful api functions. Some help with getting promises with Node async functions. Read the Adapting Node section of the readme to see how it is done.
JSFiddle Demo demonstrating in browser use of q
I think this is a really stupid question but I'm having a hard time wrapping my head around promises.
I'm using Q (for nodejs) to sync up a couple of async functions.
This works like a charm.
var first = function () {
var d = Q.defer();
fs.readdir(path,function(err,files){
if(err) console.log(err);
d.resolve(files);
});
return d.promise;
};
var second = function (files) {
var list = new Array;
files.forEach(function(value, index){
var d = Q.defer();
console.log('looking for item in db', value);
db.query(
'SELECT * FROM test WHERE local_name =? ', [value],{
local_name : String,
},
function(rows) {
if (typeof rows !== 'undefined' && rows.length > 0){
console.log('found item!', rows[0].local_name);
d.resolve(rows[0]);
} else {
var itemRequest = value;
getItemData(itemRequest);
}
}
);
list.push(d.promise);
});
return Q.all(list);
};
first()
.then(second)
.done(function(list){
res.send(list);
});
The problem I have is with this little function:
getItemData(itemRequest)
This function is filled with several of callbacks. The promise chain runs through the function just fine but ignores all the callbacks I use ( eg several XHR calls I make in the function).
A simplified version of the function looks like this (just to give you an idea):
function getItemData(itemRequest){
helper.xhrCall("call", function(response) {
var requestResponse = JSON.parse(response)
, requestInitialDetails = requestResponse.results[0];
downloadCache(requestInitialDetails,function(image) {
image = localImageDir+requestInitialDetails.image;
helper.xhrCall("call2", function(response) {
writeData(item,image,type, function(){
loadData(item);
});
});
} else {
writeData(item,image,type, function(){
loadData(item);
});
}
});
});
The xhr function I use looks like this:
xhrCall: function (url,callback) {
var request = require("request")
, colors = require('colors');
request({
url: url,
headers: {"Accept": "application/json"},
method: "GET"
}, function (error, response, body) {
if(!error){
callback(body);
}else{
console.log('Helper: XHR Error',error .red);
}
});
}
So my questions:
Can I leave the function unaltered and use the callbacks that are in place ánd the promise chain?
Or do I have to rewrite the function to use promises for the XHR?
And if so, How can I best write my promise chain? Should I reject the initial promise in the forEach?
Again, sorry if this is a really stupid question but I don't know what the right course of action is here.
Thanks!
[EDIT] Q.nfcall, I don't get it
So I've been looking into Q.nfcall which allows me to use node callbacks. Bu I just don't understand exacly how this works.
Could someone give a simple example how I would go about using it for a function with several async xhr calls?
I tried this but as you can see I don't really understand what I'm doing:
var second = Q.nfcall(second);
function second (files) {
[EDIT 2]
This is the final funcction in my getitemdata function callback chain. This function basically does the same as the function 'second' but I push the result directly and then return the promise. This works as stated, but without all the additional callback data, because it does not wait for the callbacks to return with any data.
function loadData(item) {
var d = Q.defer();
db.query(
'SELECT * FROM test WHERE local_name =? ', [item],{
local_name : String,
},
function(rows) {
if (typeof rows !== 'undefined' && rows.length > 0){
list.push(d.promise);
}
}
);
});
return Q.all(list);
};
Your answer is not really clear after your second edit.
First, on your orignal question, your getItemData has no influence on the promise chain.
You could change you the function's call signature and pass your deferred promise like so.
getItemData(itemRequest, d)
and pass this deferred promises all the way to your xhrCall and resolve there.
I would re-write your whole implementation and make sure all your functions return promises instead.
Many consider deferred promises as an anti-pattern. So I use use the Promise API defined in harmony (the next javascript)
After said that, I would re-implement your original code like so (I've not tested)
var Promise = Promise || require('es6-promise').Promise // a polyfill
;
function errHandler (err){
throw err
}
function makeQuery () {
var queryStr = 'SELECT * FROM test WHERE local_name =? '
, queryOpt = {local_name: String}
;
console.log('looking for item in db', value)
return new Promise(function(resolve, reject){
db.query(queryStr, [value], queryOpt, function(rows) {
if (typeof rows !== 'undefined' && rows.length > 0){
console.log('found item!', rows[0].local_name);
resolve(rows[0]);
} else {
// note that it returns a promise now.
getItemData(value).then(resolve).catch(errHandler)
}
})
})
}
function first () {
return new Promise(function(resolve, reject){
fs.readdir(path, function(err, files){
if (err) return reject(err)
resolve(files)
})
})
}
function second (files) {
return Promise.all(files.map(function(value){
return makeQuery(value)
});
}
first()
.then(second)
.then(res.send)
.catch(errHandler)
Note that there is no done method on the Promise API.
One down side of the new Promise API is error handling. Take a look at bluebird.
It is a robust promise library which is compatible with the new promise API and has many of the Q helper functions.
As far as I can tell, you need to return a promise from getItemData. Use Q.defer() as you do in second(), and resolve it when the callbacks complete with the data. You can then push that into list.
To save code, you can use Q.nfcall to immediately call a node-style-callback function, and return a promise instead. See the example in the API docs: https://github.com/kriskowal/q/wiki/API-Reference#qnfcallfunc-args