I am implementing an API to manage a MySQL database from Nodejs, and I want to keep my code clean as the work I intend to do can take me to have a lot of nested connection.query functions all over the place, so I've decided to separate as many as the queries I could into separate functions.
To start I wanted to do a function to check if there is an existing element already on the table, like this:
function validator(item) {
return new Promise(function(err,done) {
connection.query('select * from items where item_id = ?', [item.id] , function(err,rows){
console.log(rows);
console.log("above row object");
if (err) {
return done(false);
}
if (rows.length) {
console.log('That item is already in the DB.');
return done(false);
} else {
console.log('That item is new.');
return done(true);
}
});
});
}
And then I want to use it like this:
var insert = function (item, done) {
validator(item).then(function(err,done) {
console.log('here I go: '+done);
if(done) {
console.log('good');
} else{
console.log('bad')
}
);
}
But it does not work like I expected, the first function accurately detects if an item exists or not, but the second one never never reaches the "here I go" print.
I have checked Use promise to process MySQL return value in node.js, Javascript Promise node.js? and Javascript & promises with Q - closure problems in promises but the conclussion I reach is that this is a classic PEBCAK problem, such as that I'm a very incompetent javascript programmer and can't wrap my head around the callbacks and promises all that well so I'm managing them the wrong way.
The first callback you pass to the Promise constructor isn't the failing one.
Change
return new Promise(function(err,done) {
into
return new Promise(function(done, reject) {
and call it like this:
validator(item).then(function(done) {
(by the way it's very confusing to call done both the resolve callback and the result).
Handle the incoming error: Change
if (err) {
return done(false);
}
into
if (err) {
reject(err);
}
and don't forget to catch those errors.
Also, it's probably outside the scope of this QA but you usually take and release connections. You should probably have a look at promises ready mysql drivers.
Related
Up until now for me the concept of a Node Worker has been one of those things that sounds interesting and I will find out about one day.
Well that day has come and I am asking for some help.
I need to call a web service and then process the data returned. I can call the service with an XMLHttpRequest, but then I have to get useful data out of it.
There is a nice node module that both calls the service and returns the data in a useful form with one call.
I can set up Node worker (in Wakanda) to do this and verify that it works.
My problem is handling the asynchronous call in the proxy.
The call to the node module looks like this:
myModule.getData(param, (err, data) => {
// data is an object containing everything I want.
// if I get data I want to return it to the proxy
// if I get an err I want to return the error to the proxy
});
So my wrapper code looks something like this:
function doSomething(param){
// call proxy with param
// wait for result
// return result
}
This all sounds like something I should know how to do. However I think I am struggling with too many new things and getting myself absolutely confused.
PS: I did try Threadify but couldn't work out how to get the worker to return the error it received.
I would really appreciate any help or pointers here.
If I am correctly understanding your issue, you cannot return a value from a function AFTER an asynchronous call completes. You need to handle the data within the myModule.getData callback.
If you would rather handle it in a calling function (like doSomething), you can use a promise to "return" a value.
function myProxy(param) {
return new Promise((resolve, reject) => {
myModule.getData(param, (err, data) => {
if (!err) { // or however you determine an error occurred.
resolve(data); // if I get data I want to return it to the proxy
} else {
reject(err); // if I get an err I want to return the error to the proxy
}
});
});
}
function doSomething(param){
myProxy(param).then(data => {
// Handle the data here.
}).catch(err => {
// Handle the error here.
});
}
I am trying to write a script that will query a local database and send the results to a Google Spreadsheet. Where I am currently stuck and looking for help is how do I get the results of my query.
var myQuery = [];
function runQuery(query, callback) {
client.connect(function (err) {
if (err) throw err;
client.query(query, function (err, result) {
if (err) {
console.log(err);
}
callback({ result: result });
client.end(function (err) {
if (err) throw err;
});
});
});
}
runQuery('SELECT item_number FROM item WHERE item_number LIKE \'11-%\' LIMIT 10', function (resultsObject) {
myQuery = JSON.stringify(resultsObject.result.rows);
console.log('Did stringify work?');
console.log(myQuery);
});
console.log(myQuery);
My output:
Info: Start process (3:46:11 PM)
[]
Did stringify work?
[{"item_number":"11-0011"},{"item_number":"11-0012"},{"item_number":"11-1255"},{"item_number":"11-0052"},{"item_number":"11-0060"},{"item_number":"11-1256"},{"item_number":"11-1281"},{"item_number":"11-0659"},{"item_number":"11-0660"},{"item_number":"11-0054"}]
Info: End process (3:46:12 PM)
I think I understand what's happening, the scope of the variable myQuery is set and printed within the runQuery function just fine, but outside of that function it's not being actually set. How do I get around this?
Very new to JavaScript and NodeJS so I hope I'm using the correct terminology.
Thanks in advance!
The empty myQuery has already been consoled before you actually put values into it. This issue is caused by Javascript's Asynchronous mechanism. If you want to do something with the result, you have to use a callback function. Just like what you did when you accessed result from runQuery.
Another way to do it is promise. You can return a promise in some function and use the promise to access result.
If you decide to use promise, here is the code:
function getresult(parameters) {
return new Promise((resolve, reject) => {
runQuery(parameter, resolve, reject);
});
}
Using this function, you passed resolve as callback to runQuery. You do want to add another err callback function to catch err in runQuery. In promise you use reject as err callback.
When you want to do something with the result:
getresult(parameters)
.then((result) => {put your code here. result variable is your result})
.catch((err) => {do whatever with err});
This is not a scope issue. It is a timing issue. The query takes some time to execute. JavaScript is 'non-blocking' meaning it will start an asynchronous process (like doing a DB query - really virtually all I/O operations) and return the result later. In your code you are using a callback that is run once the data becomes available.
So, with that in mind, the way to think about your code is that you are doing things in this order:
1) declaring your function runQuery - this happens first since in JavaScript function declarations are moved to the top of the script they are in (a process called hoisting)
2) your variable myQuery is established and initialized (to an empty array)
3) your query is kicked off by a call to runQuery; this only starts the process
4) your console.log(myQuery) at the end of your script is run
5) some time later the query completes and your callback is called with the data where your other console.log() shows the returned data
I am trying to understand how promisifyAll works. i am trying hard to understand the example given in their documentation. Can anyone please give me a simple example, how it works? I am trying to understand the concept and how we can use it. Thank you
var objects = {};
objects.first = function(){
console.log("First one is executed");
}
objects.second = function(){
console.log("second one is executed");
}
objects.third = function(){
console.log("third one is executed");
}
how can we promisify the objects variable? or something like that.
First, you need to understand how a nodeFunction works. It takes a callback function as the last argument. This callback takes two arguments, the first one is error and the second the data. Consider for example require("fs").readFile:
// This is the callback
function callback (error, data) {
if (error) {
console.error('There was an error', error)
} else {
console.log('This is the data', data)
}
}
require('fs').readFile('my-file.txt', callback)
Note that this is a convention, there's nothing in JS itself that enforces it.
Let's move to Promise.promisify now. This method takes a nodeFunction and returns a promisified version of it. This is more or less what it does:
function promisifiedReadFile (filePath) {
return new Promise(function (fulfill, reject) {
require('fs').readFile(path, function (error, data) {
if (error) { reject(error) } else { fulfill(data) }
})
})
}
It's a bit verbose but as you can see you now have a version of readFile that returns a promise instead of accepting a callback. Of course, this example is hard-coded for readFile, Promise.promisify works for any nodeFunction instead:
const promisifiedReadFile = Promise.promisify(require('fs').readFile)
The two promisifiedReadFile methods work in the same way.
Last, Promise.promisifyAll takes an object, goes through it and finds all the methods, it then applies Promise.promisify to each of them.
So, if you call Promise.promisifyAll(require('fs')) you get back a version of the fs module where all the methods return promises instead of accepting callbacks.
About your example I'm not sure what you're trying to achieve but the methods you defined are not nodeFunctions so they can't be promisified.
I have a feeling that I'm just trying to fit a square peg into a round hole, but I'm trying to apply some things with Angular2 and Typescript, and I'm banging my head against a wall.
I've written a Javascript module that acts as an API client library to an API I'm consuming. It just packages some convenience things like setting up the correct API keys, switching keys based on certain desired data, etc. It's basically just a convenience library.
Most of the methods follow a pattern where you provide a query term and then execute a callback.
So for example:
API.searchAutocomplete("angular", function(err, data) {
// handle the data/error
});
Inside that method:
searchAutocomplete: function(query, callback) {
// set up request with data payload, url, headers, etc
$.ajax(settings)
.done(function(response) {
// callback with success
})
.fail(function () {
// callback with error
});
}
I'm struggling with trying to understand how to run this function in Typescript in an Angular service with a Promise (square peg round hole). Or should I just pass a callback within the service and treat it like it's Javascript?
My attempt:
public getAutocomplete(query:string): Promise < any > {
return new Promise((resolve, reject) => {
API.searchAutocomplete(query, function (err, result) {
if (err) {
reject(err);
return;
}
resolve(result);
});
});
}
Second, I've been able to load the library into my Angular app but I can't seem to actually make any of the requests. Even if I break in the console and access the library object it doesn't seem to actually make any network requests. Which I really don't understand.
Edit: I've sorted this part out.
When I made my service call return a promise, I had to subscribe to the promise otherwise I wouldn't execute it correctly. I think I still need to understand how to write my service call to return an observable and map the callback response.
As expected, I was trying to do more work than I should have.
This is pretty simple, just return an observable that calls the external library.
public autoCompleteResults(query: string): Observable<string[]> {
return new Observable<string[]>(observer => {
API.searchAutocomplete(query, function (err, result) {
if (err) {
console.log(err);
observer.next([]);
// OR
observer.error(err);
return;
}
observer.next(result);
});
});
}
I have a file where I'm writing things:
var stream = fs.createWriteStream("my_file.txt");
stream.once('open', function(fd) {
names.forEach(function(name){
doSomething(name);
});
stream.end();
});
This is working ok and I'm able to write to the file.
The problem is that the doSomething() function has some parts that are asynchronous. An example can be given with the dnsLookup function. Somewhere in my doSomething() I have:
dns.lookup(domain, (err, addresses, family) => {
if(err){
stream.write("Error:", err);
}else{
stream.write(addresses);
}
});
Now, my problem is, since the DNS check is asynchronous, the code keeps executing closing the stream. When the DNS response finally comes it cannot write to anywhere.
I already tried to use the async module but it didn't work. Probably I did something wrong.
Any idea?
Now that NodeJS is mostly up to speed with ES2015 features (and I notice you're using at least one arrow function), you can use the native promises in JavaScript (previously you could use a library):
var stream = fs.createWriteStream("my_file.txt");
stream.once('open', function(fd) {
Promise.all(names.map(name => doSomething(name)))
.then(() => {
// success handling
stream.end();
})
.catch(() => {
// error handling
stream.end();
});
});
(The line Promise.all(names.map(name => doSomething(name))) can be simply Promise.all(names.map(doSomething)) if you know doSomething ignores extra arguments and only uses the first.)
Promise.all (spec | MDN) accepts an iterable and returns a promise that is settled when all of the promises in the iterable are settled (non-promise values are treated as resolved promises using the value as the resolution).
Where doSomething becomes:
function doSomething(name) {
return new Promise((resolve, reject) => {
dns.lookup(domain, (err, addresses, family) => {
if(!err){ // <== You meant `if (err)` here, right?
stream.write("Error:", err);
reject(/*...reason...*/);
}else{
stream.write(addresses);
resolve(/*...possibly include addresses*/);
});
});
});
There are various libs that will "promise-ify" Node-style callbacks for you so using promises is less clunky than the mix above; in that case, you could use the promise from a promise-ified dns.lookup directly rather than creating your own extra one.