ES7 - how can I replace Promise with await? - node.js

One thing puts me off with Promise is that it is difficult to grasp with resolve and reject. Also the need of wrapping for Promise is ugly. Unless you use it very often, I tend to forget how to use them over time. Besides, code with Promise is still messy and hard to read. Hence I don't really like using it at all - because it has not much different from the callback hell. So I thought with ES7 await, I can avoid using Promise and giving me some more faith in JavaScript, but it seems that it is not the case. For instance:
const getOne = async () => {
return Weather.findOne(options, function(err, weather) {
//
});
}
const getWeather = async () => {
const exist = await getOne();
if (exist == null) {
return new Promise(function(resolve, reject) {
// Use request library.
request(pullUrl, function (error, response, body) {
if (!error && response.statusCode == 200) {
// Resolve with the data.
resolve(body);
} else {
// Reject with the error.
reject(error);
}
});
});
}
}
const insertWeather = async () => {
try {
const data = await getWeather();
} catch (err) {
res.set('Content-Type', 'application/json');
return res.status(200).send('Error occurs: ' + err);
}
}
insertWeather();
request(pullUrl, function (error, response, body) {} is an AJAX call from request package for nodejs.
I have to wrap it with Promise - but not precede it with await. Ideally this is what I imagine:
return await request(pullUrl, function (error, response, body) {...}
But if I do that, I will get the request object in return, instead of the data return from the request package - at this line:
const data = await getWeather();
Any ideas or solutions to avoid using Promise in the case such as the one above?

You'll find bluebird and node.js now come with promisify to allow you to consume Node callbacks as promises when you need too. Alternatively, you can consider a functional reactive approach and use a library like RxJS which will handle promises, node callbacks and other data types into streams.
const promisify = require('utils').promisify // alternatively use bluebird
const request = require('request-promise');
const weatherFn = promisify(Weather.findOne);
const weatherOptions = {};
async function getWeatherIfDoesNotExist() {
try {
const records = await weatherFn(weatherOptions);
if (records === null) {
return await request('/pullUrl');
}
} catch(err) {
throw new Error('Could not get weather');
}
}
async function weatherController(req, res) {
try {
const data = await getWeatherIfDoesNotExist();
} catch (err) {
res.set('Content-Type', 'application/json');
return res.status(200).send('Error occurs: ' + err);
}
}
function altWeatherController(req, res) {
return getWeatherIfDoesNotExist()
.then((data) => { // do something })
.catch((err) => {
res.set('Content-Type', 'application/json');
return res.status(200).send('Error occurs: ' + err);
})
}

As mentionned in the documentation here, you will need to wrap request with interfaces wrappers like request-promise (or you can find alternatives interface in the documentation) in order to return a Promise from request.

Related

return the content of a request using callback in node.js?

How to return the content of a request using callback in node.js ?
I would like to make the program to wait until it has fetched the result of the inner callback requestCallBack, hence i have put an await keyword infront of requestCallback inside the request function, but it has no effect. I know it can work with promises, I just want to practice callbacks in node.js.
const getData = url => {
return await request(url, await requestCallBack);
};
const requestCallBack = async (error, response, body) => {
if (!error && response.statusCode === 200) {
return body;
}
};
The Request library does not return a Promise
async-await will work with those libraries only which return a Promise
Use Node-Fetch instead
const fetch = require('node-fetch');
// Then inside some async function...
const response = await fetch(`${YOUR_API}`, {
method: 'GET', // or POST or whatever
headers: {
"Some-key": "Some-value"
}
});
const responseJson = await response.json();
// Apply exception handling
Also practicing callbacks, syntactically is...
// Define
const someAsyncFn = (x, y, callback) => {
...
...
// After work's done, invoke callback
callback();
}
// Invoke
someAsyncFn("some-val", "another-val", () => {
// Things to do in the callback
...
...
})
// Also you can parameterize the callback function

wait untill for loop not executed fully in node js

I want to send my response when I executed my for loop but it is sending directly without wait in for loop
I have tried with different way and apply wait in async function but it goes to next
async (request, response) => {
let imageErr = [],
succArr = [];
/** Image processing and send to s3 */
const OriginalUrl = request.body.images;
for (const image of OriginalUrl) {
await createImage.createWebp(image, 'webp', (err, result) => {
if (err) {
imageErr.push(err);
} else {
succArr.push(result);
console.log(succArr);
}
});
/** webp */
await createImage.createJpeg(image, 'jpeg', (err, result) => {
if (err) {
imageErr.push(err);
} else {
succArr.push(result);
console.log(succArr);
}
});
}
if (succArr === 12) responseUtil.success(response, succArr);
else responseUtil.error(response, 'Fail');
};
I expect send response after the for loop is fully executed
It seems you use the kind of framework that accepts a callback instead of returning a Promise (which is necessary for await to work properly). One way is to wrap your call into a Promise. Try something like this:
for (const image of OriginalUrl) {
await new Promise((res, rej) => {
createImage.createWebp(image, 'webp', (err, result) => {
if (err) {
imageErr.push(err);
} else {
succArr.push(result);
}
res(); // <-- Important
});
});
(...)
}
You may also utilize rej but then you would have to catch it as an exception. Your choice.
Side note: if (succArr === 12), did you mean if (succArr.length === 12)? Also, I assume this is for debug, because it definitely is not a good idea to hardcode literal 12.

How to return value then-catch block

My Codes below;
I've a then-catch block. My responseArray is a global variable. i got response from functionName function; but i can't use result out of then block. How can i use then response out of block?
My Codes below;
I've a then-catch block. My responseArray is a global variable. i got response from functionName function; but i can't use result out of then block. How can i use then response out of block?
module.exports = {
foo1: function(param){
return new Promise((resolve,reject) => {
var result = //some code here
resolve(result);
});
},
foo2: function(param){
return new Promise((resolve,reject) => {
this.foo1('abc').then(function(res){
let response = {
'item':'ok',
'result':res.some_field
};
console.log(response); // its ok here.
responseArray.push(response); //its ok here too
}).catch(err =>{
console.log(err);
reject(err);
});
console.log(responseArray); //nothing in array here
resolve(responseArray);
});
}
};
First thing to remember is that promises are asynchronous. Promises are doing exactly what they say, you are essentially signing a contract (promise) that you will get your data (or error) but not synchronously, but at some time in the future when the computations have finished.
In order to access your responseArray you will need to resolve your foo2 promise (inside of .then) and continue the promise chain by calling it, i.e.
module.exports = {
foo1: function(param){
return new Promise((resolve,reject) => {
var result = //some code here
resolve(result);
});
},
foo2: function(param){
return new Promise((resolve,reject) => {
this.foo1('abc').then(function(res){
let response = {
'item':'ok',
'result':res.some_field
};
console.log(response); // its ok here.
responseArray.push(response); //its ok here too
resolve(responseArray) // resolve the promise inside of .then
}).catch(err =>{
console.log(err);
reject(err);
});
});
}
};
foo2('someValue').then(response => {
console.log(response) // this will be your array
})
Also, as a side note, ensure you are not falling into the trap of the promise constructor anti-pattern. This is where you unnecessarily turn synchronous code into asynchronous code just for the sake of using "promises"
For example, a valid use of a promise would be to convert a callback, like so:
const getFile = filename => {
return new Promise((resolve, reject) => {
fs.readFile(filename, 'utf8', (err, data) => {
if (err) reject(err)
resolve(data)
})
})
}
whereas this is unnecessary:
const printData = data => {
return new Promise((resolve, reject) => {
resolve(console.log(data))
})
}
vs
const printData = data => {
console.log(data)
}
Read more here: What is the explicit promise construction antipattern and how do I avoid it?

Nodejs Executing Functions

Im new to Nodejs and was wondering why the functions execute out of order instead of how ive written it:
var tor_proxy = require("tor-request")
var s = require("sleep");
tor_proxy.setTorAddress("localhost", 9050);
tor_proxy.TorControlPort.password = "password";
function ShowIP() {
tor_proxy.request("http://ident.me", function(err, response, body) {
if(!err && response.statusCode == 200) {
console.log(body);
}
});
}
function Renew() {
tor_proxy.renewTorSession(function() { console.log("renewed"); });
}
ShowIP();
Renew();
ShowIP();
//Id Like It To Show The IP Then Renew Then Show The New IP
//But Instead It's Out Of Order
Nodejs is event driven (correct me if im wrong) and any help will be appreciated. Thanks :)
The script will be executed like this:
Inside ShowIP(), tor_proxy.request() sends a request to http://ident.me .
Without waiting for any reply from http://ident.me, function Renew() is executed.
tor_proxy.renewTorSession() is likely to be an asynchronous function. If so, after it begins, the next ShowIP() will be executed without waiting for renewTorSession() to complete.
Depending on how fast http://ident.me replies and how fast renewTorSession() completes, the results may vary.
To execute these functions in proper order, you can search for the following keywords:
Promise
Async/Await
util.promisify() from Node.js
Libraries like Async.js
An example using promise, async and await:
var tor_proxy = require('tor-request');
tor_proxy.setTorAddress('localhost', 9050);
tor_proxy.TorControlPort.password = 'password';
function ShowIP() {
return new Promise((resolve, reject) => {
tor_proxy.request('http://ident.me', function (err, response, body) {
if (err) reject(err);
else if (response.statusCode !== 200) reject('response.statusCode: ' + response.statusCode);
else {
console.log(body);
resolve();
}
});
});
}
function Renew() {
return new Promise((resolve, reject) => {
tor_proxy.renewTorSession(() => {
console.log('renewed');
resolve();
});
});
}
async function testFunction() {
// Await makes sure the returned promise completes before proceeding.
// Note that await keyword can only be used inside async function.
try {
await ShowIP();
await Renew();
await ShowIP();
console.log('done!');
} catch (error) {
console.log(error);
}
}
testFunction();

Nest multiple async await

I have the following Express endpoint:
const all = require('promise-all');
router.post('/verify', upload.single('photo'), async (req, res) => {
...
await all({'p1': p1, 'p2': p2}).then((response) => {
...
console.log("Response:",
ruleCtrl.manageRule(detection, res);
});
});
ruleCtrl.manageRuleis as follows:
export async function manageRule(identifierDetected, res) {
let rule = db.getRule(identifierDetected);
await all([rule]).then((ruleExtracted) => {
...
res.json(ruleExtracted);
}).catch((err) => {
res.status(418).send("DOCUMENT_NOT_RECOGNIZED");
});
}
and db.getRule:
export async function getRule(idRule) {
return new Promise((resolve, reject) => {
Rule.findOne({ruleID: idRule}, (err, rule) => {
if (err) {
reject("MongoDB Rule error: " + err);
} else {
resolve(rule);
}
});
})
}
My response is into manageRule and this function depends of the values extracted into the await all. So, right now, Express is returning a response before get the information from mongoose database (db).
Which is the way to handle this issue?
Thanks everyone!
I would refactor your code a bit to make it easier to read, and also return the result from ruleCtrl.manageRule(detection, res);.
The request might simply be timing out since your original code is missing a return there or an await (to make sure it finishes executing)
Express endpoint:
const all = require('promise-all');
router.post('/verify', upload.single('photo'), async (req, res) => {
...
// Catch any exceptions from the promises. This is the same as using .catch
try {
// Lets assign the returned responses to variable
let [p1Result, p2Result] = await all({'p1': p1, 'p2': p2});
...
console.log("Responses:", p1Result, p2Result);
// return the response from manageRule method
return ruleCtrl.manageRule(detection, res);
} catch(err) {
// Handle err here
}
});
One of the great benefits with async await is moving away from chained promises, so simply return the result from the await to a variable instead of using .then()
ruleCtrl.manageRule
export async function manageRule(identifierDetected, res) {
// Use try catch here to catch error from db.getRule. Assign to variable and return
// res.json
try {
let ruleExtracted = await db.getRule(identifierDetected);
...
return res.json(ruleExtracted);
} catch(err) {
return res.status(418).send("DOCUMENT_NOT_RECOGNIZED");
}
}
You dont have to return res.json or res.status here, I just like to keep track of when I want to end function execution.
You could refactor the ruleCtrl.manageRule method even further by not sending in res as a parameter but by returning the result from db.getRule instead. Let router.post('/verify) handle req and res, so to make it even easier to read.

Resources