Background:
I have a gateway that returns the status code 200 via a post request if it is completely booted. If not it returns 500 while booting.
My idea:
I have a NodeJS application which should wait until the gateway returns 200. So I created a while loop which checks the state of the gateway.
My problem:
Unfortunately nothing works, the state is always true. Non of the log statements in the request will be display.
Do you have tips for me how I can fix this?
while (isGatewayUnavailable()) {
log.info('waiting for gateway ...');
sleep(60)
}
function isGatwayUnavailable() {
const url = '...'
let state = true
request.post(url, (err, res, body) => {
log.debug(0)
if (err) {
log.debug("Gateway offline");
log.debug("err: " + err);
}
else if (res.statusCode === 200) {
log.debug("Gateway online");
state = false;
cb(true);
}
else {
log.debug("Status Code: " + res.statusCode);
}
});
log.debug('return state: ' + state);
return state;
}
There is no "waiting" in JS. There's only "running code" and "running code in response to signals" (events, callbacks, promises). In this case, you want to do something based on a process that you do not control the timing of, so you can't use a synchronous function: by the time the function reaches its return keyword, you don't have any information to return yet..
So, instead of making your function return a value and having the caller wait for that value, make your code "do things once the information is in". That is, make your function either generate an event that you have a handler registered for, or pass a callback as argument so that your function can run that callback once it has the information necessary, or have it return a promise whose resolve (or reject) gets called once you have the information necessary.
1. Event-based:
const pubsub = ...;
function checkGatwayAvailability() {
request.post(url, (err, res, body) => {
pubsub.signal("gateway:availability", { available: ..., error: ... });
});
}
with caller code:
const pubsub = ...;
pubsub.register("gateway:availability", data => {...});
...
checkGatewayAvailability();
In this, the code that calls this and the code that handles the result are 100% detached from each other. Also note that pubsub isn't a real thing. Depending on your framework and APIs, there will be different ways to achieve event generation/handling, or you might even need to write your own (which really means "hit up npm and find one that is well documented and used by many folks, then use that").
2. Using a callback:
function checkGatwayAvailability(reportResult) {
request.post(url, (err, res, body) => {
reportResult({ available: ..., error: ... });
});
}
with caller code:
checkGatwayAvailability( result => {
...
});
In this approach, the calling and handling code are coupled in the sense that your call points to the handler, even if your handler is declared somewhere completely different, like:
checkGatwayAvailability(NetworkMonitor.handleGatewayResponse);
3. Using a promise:
function checkGatwayAvailability(reportResult) {
return new Promise((resolve, reject) => {
request.post(url, (err, res, body) => {
if (err) reject(err);
resolve(...);
});
});
}
with caller code:
checkGatwayAvailability().then(result => {...}).catch(err => {...});
Similar to a callback, the calling and handling code are coupled, but with promises you don't "guess" at whether the resultant information is good or bad, you literally have separate code paths for the "good" cases (handled by then), and the "bad" cases (handled by catch).
3b. Using a promise through async/await syntax:
In this case, request.post does not return a Promise, your function would still need to bake its own promise, so using an async declaration doesn't make a lot of sense. We can still use the await keyword in the calling code, though:
try {
const result = await checkGatwayAvailability();
} catch (e) {
...
}
but only if that caller code itself runs inside an async context.
Related
I just realized all of my test code has a race condition.
My style pattern follows something like this:
const myFunc = (callback) => {
return somePromise().then((result) => {
return someOtherPromise();
}).then((result) => {
db.end(() => {
callback();
});
}).catch((err) => {
db.end(() => {
callback(err);
});
});
};
I'm testing with Jest. Test code looks something like this.
it('should work', (done) => {
// mock stuff
let callback = () => {
expect(...);
done();
};
myFunc(callback);
});
I have dozens of functions and tests following this pattern. The last test I wrote was giving me a Jest matcher error in my callback. After much confusion, I realized that the first callback execution is throwing the Jest failure, and the callback with the err parameter is being executed and failing before done() is called by the first callback execution.
I'm realizing this pattern might be absolutely terrible. I've managed to beat the race condition by having certain expect() calls in certain order, but that's no way to do this.
How can I remove the potential for the race condition here?
I'm open to completely changing the style I do this. I know my Javascript isn't particularly amazing, and the system is still pretty early in its development.
My colleague advised me that this is a good case to use async/await.
See a new version of the code under test:
const myFunc = async (callback) => {
let other_result;
try {
let result = await somePromise();
other_result = await someOtherPromise(result);
} catch (err) {
db.end(() => {
callback(err);
});
return;
}
db.end(() => {
callback(null, other_result);
});
};
I updated things a bit to make it seem more real-world-ish.
I know this makes myFunc return a promise, but that's okay for my use-case. By doing this, I'm ensuring that the callback is only executed once, preventing the Jest error from getting caught up elsewhere.
EDIT:
I'm realizing that this is the same as if I had moved the catch block to be before the final then block, I would have the same behavior :/
I'm trying to learn pomise and I used code which I found in Internet... I don't understand everything. It looks for me very nasty but it works...
I initialized promise
function initialize(city) {
var options = {
url: 'https://api.weatherbit.io/v2.0//forecast/',
headers: {
'User-Agent': 'request'
}
};
return new Promise(function(resolve, reject) {
request.get(options, function(err, resp, body) {
if (err) {
reject(err);
} else {
resolve(JSON.parse(body));
}
})
})
}
I don't understand why I need to put return after initializePromise. Is it possible to refactor the code without return?
var initializePromise = initialize("Berlin");
return initializePromise.then(function(result) {
weather = result;
var rain = result["data"][0]["precip"];
console.log(rain);
}, function(err) {
console.log(err);
})
This all depends upon what you want to do. If you wrote this version, which is slightly altered from your original but functionally the same:
function f1(city) {
return initialize(city).then(function(result) {
const weather = result
const rain = result["data"][0]["precip"];
console.log(rain)
}, function(err) {
console.log(err)
})
}
then when you call
f1('Berlin')
you would request the Berlin result from the server, When the server responds, you would either pass to console.log the error received from the request or turn the returned body into a JS object, extract the appropriate precip property from it, and log that to the console. The resulting Promise value returned from f1 is useless, and the weather variable is unused and unusable.
If you want to log that precipitation, but still keep a useful return value, you can write:
function f2(city) {
return initialize(city).then(function(result) {
const weather = result
const rain = result["data"][0]["precip"];
console.log(rain)
return weather // *** NOTE new line here ***
}, function(err) {
console.log(err)
})
}
This time calling with Berlin (and ignoring the error case from now on), you would log the precipitation returned, but also return a Promise for the whole Berlin weather node. That means you can still do this:
f2('Berlin')
to log Berlin's first precipitation value, but that now returns a useful value, so you could do
f2('Berlin').then(console.log)
to do that same logging, and then log the entire Berlin result.
Or you could do
f2('Berlin').then(function(weather) {
// do something useful with `weather` here.
}, errorHandler)
But now note the cleanup that is available. First of all the rain variable in f2 is only used on the next line, and the weather one is simply a reference to the original result argument. So we can simplify it this way:
function f3(city) {
return initialize(city).then(function(result) {
console.log(result["data"][0]["precip"])
return result
}, function(err) {
console.log(err)
})
}
This does the same thing more simply. But now there is a much more important simplification. It's quite possible that we don't need this function at all! If we have an error handler of some sort (even if it's just console.err), and we already have a function that does most of the weather handling we want, then instead of
f3('Berlin').then(function(weather) {
// do something useful with `weather` here.
}, errorHandler)
we can add the logging line from f3 into this first callbak, and get the same result by calling directly to initialize:
initialize('Berlin').then(function(weather) {
console.log(weather["data"][0]["precip"])
// do something useful with `weather` here.
}, errorHandler)
The reason this works is because initialize returns the result of calling then on the Promise, and then f2 and f3 also return either an altered value or the original one, keeping a Promise chain intact.
I would suggest that if you're in doubt you return something in any of these situations. It makes it much easier to continue working with values.
I am trying to use a promise to call a function getTweets.
Not using an AJAX call, but a simple promise 'call' from 1 javascript file to another.
The function works, but i keep getting 'undefined'.
I have read dozens of questions here on stackoverflow and have spent days
to understand promises, but still can't solve it.
var Twit = require('twit') // Imports the Twitter library
require('dotenv').config() // to get the environment vars into the app
// This is the function:
function getTweets (screen_name) {
let T = new Twit({ /* <twitter key and token here> */ });
T.get('statuses/user_timeline', { screen_name: screen_name, count: 3}, function (err, data, response) {
let myTweets = [];
for (let i in data) {
let text = data[i].text;
myTweets.push(text);
}
return myTweets;
})
}
module.exports.getTweets = getTweets;
And this is the promise that tries to get the tweets:
var promise = tweets.getTweets('dizid');
promise.then(
console.log(myTweets),
console.log(err))
// gives error: promise.then(
// ^
// TypeError: Cannot read property 'then' of undefined
Any help greatly appreciated.
Your problem is that you never return anything from your getTweets() function even though it needs to return a promise. The function calls T.get() and pass it a callback function. You return from this callback function but this doesn't do anything, it doesn't mean that this value gets returned from getTweets().
This is a pretty common mistake when it comes to working with asynchronous calls. What needs to be done is to make getTweets() return a promise that gets resolved when it should.
When working with asynchronous calls that don't implement the promise interface, you need to wrap this call with a new promise. Your getTweets() function should then look like this:
function getTweets (screen_name) {
let T = new Twit({ /* <twitter key and token here> */ });
return new Promise(function(resolve, reject) {
T.get('statuses/user_timeline', { screen_name: screen_name, count: 3}, function (err, data, response) {
if (err) {
reject(err); // Reject the promise since there was an error
} else {
let myTweets = [];
for (let i in data) {
let text = data[i].text;
myTweets.push(text);
}
resolve(myTweets); // Resolve the promise with the result
}
});
});
}
However, it seems the Twit API does support the promise interface, so instead of providing a callback function you can just use the promise created by T.get(). HMR's answer explains how to do this.
Another mistake you've made is with this code:
promise.then(
console.log(myTweets),
console.log(err))
The way you've written it, it reads "Run console.log(myTweets) and console.log(err), then invoke promise.then() with the result of the former as the first argument and the result of the latter as the second argument.
then() takes callback functions (which get invoked depending on the resolving/rejection of the promise) as arguments, so the code should look like this:
promise.then(
function(myTweets) {
console.log(myTweets);
},
function(err) {
console.log(err);
});
Async/await
If you're interested in taking things further, the modern approach to working with asynchronous code is async/await, which is syntactic sugar for promises that lets you write asynchronous code more similar to regular synchronous code.
A function marked as async will implicitly return a promise, but you write it as if you return a regular value. Using the await keyword inside an async function will implicitly wait for a promise to resolve and unwrap the resolved value. The main practical benefits of this is that you can use asynchronous calls in loops and handle errors with regular try-catch blocks. Your getTweets() function would look like this using async/await:
async function getTweets(screen_name) {
let T = new Twit({ /* <twitter key and token here> */ });
const data = await T.get('statuses/user_timeline', { screen_name: screen_name, count: 3});
// Let's also use map() instead of a for loop
let myTweets = data.map(function(item) { return item.text; });
return myTweets;
}
Since get seems to return a promise you don't need to use a callback. Get Tweets can look something like this:
// in getTweets
return T.get(
'statuses/user_timeline',
{ screen_name: screen_name, count: 3}
).then(
function (data) {
console.log("data:",JSON.stringify(data,undefined,2));
return data.map(item=>item.text);
}
)
// exports the function getTweets so that other modules can use it
module.exports.getTweets = getTweets;
If that didn't work please let us know what the output of the program is (update question).
You can call getTweets like so:
tweets.getTweets('dizid')
.then(
myTweets=>
console.log(myTweets),
err=>
console.log(err)
)
I think you forget add function like
promise.then(function(res){
//code
}
Your .then() should include a call back function.
promise.then( res => {
console.log(res);
});
edit: I'm using an ES6 syntax for arrow functions, in case you're new to that.
The following code:
function handleError(res, statusCode) {
statusCode = statusCode || 500;
return function(err) {
res.status(statusCode).send(err);
};
}
function respondWithResult(res, statusCode) {
statusCode = statusCode || 200;
return function(entity) {
if (entity) {
res.status(statusCode).json(entity);
}
};
}
// Creates a new Store in the DB
export function create(req, res) {
// create user
let user = req.body.user;
let store = req.body.store;
auth.hash(user.password)
.then(hash => {
user.password = hash;
// Create user, then create store, attach store object id to user, and attach user object id to store
User.create(user)
.then(userRes => {
store.owner = userRes._id;
store.memebers = [];
store.memebers.push(store.owner);
Store.create(store)
.then(storeRes => {
return respondWithResult(res, 201);
})
.catch(err => handleError(err));
})
.catch(err => handleError(err));
})
.catch(err => handleError(err));
}
prints the error mentioned in the title, "(node:5540) Warning: a promise was created in a handler but was not returned from it". I have tried changing and tweaking the code but the error still persists.
This warning is because your code is creating promises inside of .then() handlers, but not returning them from those handlers.
Change:
User.create(user)
to:
return User.create(user)
And, change:
Store.create(store)
to
return Store.create(store)
When you don't return these promises that are created inside .then() handlers, they become separate, independent promise chains and are not linked to the previous promise chain. This is usually a programming mistake which is why Bluebird makes it a warning.
When you return them, then they add to the promise chain and thus the parent promise waits for their completion before continuing on with the chain.
I'd also suggest you probably want to change:
auth.hash(user.password)
to:
return auth.hash(user.password)
So that the caller of create() can tell when everything is done.
And, you only need one .catch() handler at the highest level. Rejected promises propagate up to the top level for you automatically (one of the things that makes error handling when using promises easier).
This is a warning message when you don't return to a request. Of course this seems just another warning but when you work on a big application this becomes a very big headache because this will lead to a memory leak and wont release the memory until you restart your app or might crush your server.
you also need to return for else statments:
function respondWithResult(res, statusCode) {
statusCode = statusCode || 200;
return function(entity) {
if (entity) {
res.status(statusCode).json(entity);
}else{
//you should write an else statement also
//maybe something like this
res.status(statusCode).send(err);
}
};
}
Return to your request in every case.
So I have started with express.js - my first JS web dev framework. I didn't make anything small, but started a bigger project. I'm learning, and building at the same time.
Coming from a Python/Flask background, express seems very complicated.
Like in python, if I want a helper method, I can just put it on top of the file, or in a new module, and import it. Super easy. But in node/express, things are async, and everything is in exports or module.exports (??). Where do helper methods go? How do I call them with callbacks?
In another question I asked, I was doing the same kind of computation multiple times. In Python, I would write a method (with if statements and parameters), and call it multiple times, using a for.. in... loop. The code I have right now is very redundant.
How do I do it in express? What are best practices for writing express code?
It really depends of what your helper is doing. If it operates with data which is passed as a parameter to it then you may save it in an external module and use require to access it.
// helpers/FormatString.js
module.exports = function(str) {
return str.toUpperCase();
}
// app.js
var formatter = require("./helpers/FormatString");
However, if you need to modify the request or the response object then I'll suggest to define it as a middleware. I.e.:
app.use(function(req, res, next) {
// ... do your stuff here
});
#Krasimir gave a correct answer. Regarding your question how to deal with asynchronous helper functions I can give you an example (not the usual foo/bar, but one of my own helper functions):
var cached; // as modules can act like singletons I can share values here
module.exports = function fetchGoogleCerts(options, callback) { // <-- callback
var now = Date.now();
if (ttl > now && cached) {
callback(null, cached); // <-- return with success
return;
}
request({
uri: options.certsUrl || defaultCertsUrl,
strictSSL: true
}, function (err, response, body) {
var error, certs;
if (!err && response.statusCode === 200) {
certs = jsonParse(body); // a local function
if (!certs) {
callback('parse_error', null); // <-- return an error
return;
}
cached = certs;
// ... more code
callback(null, cached); // <-- success case
} else {
error = {
error: err,
statusCode: response.statusCode
};
log.error(error, 'error fetching google certs');
callback(error); // <-- return an error
}
});
}
};
And using the helper:
fetchGoogleCerts = require('./google-certs.js'),
module.exports = function fetchCertsAndDecodeIdToken(token, options, callback) {
fetchGoogleCerts(options, function (err, googleCerts) {
if (err) {
callback({
errorCode: codes.io_error,
errorMsg: 'Unable to fetch Google certificates',
error: err
});
return;
}
decodeAndVerifyGoogleIdToken(googleCerts, token, options, callback);
});
};
As you can see above, the simple solution is to provide a callback function to your asynchronous helper function.
Of course you can also export an Object which extends EventEmitter, then you might not need a callback function, but register for the events. Here is an Example for a helper which emits events.