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.
Related
Today, when I was working with node, I met some special async functions with "overloads" that accept both promises and callbacks. Like this:
doSomething(result => {
console.log(result)
})
doSomething()
.then(result => console.log(result))
And probably this:
const result = await doSomething()
console.log(result)
I tried to implement this in my code but was unsuccessful. Any help would be appreciated.
You can make a function like this by creating a promise, then chaining on that promise with the argument if there is one, and then returning that chained promise. That will make it call the callback at the appropriate time, as well as giving you access to the promise that will complete when the callback completes. If you want the original promise even when there's a callback (not the chained version), then you can return that instead, by still chaining but then returning the original promise instead.
function f(cb) {
let promise = new Promise((resolve, reject) => {
setTimeout(() => resolve(123), 1000);
});
if (cb) {
return promise.then(cb);
} else {
return promise;
}
}
// usage 1
f(console.log)
// usage 2
f().then(console.log)
Let's say you had a function that was going to read your config file and parse it and you wanted to support both versions. You could do that like this with two separate implementation inside. Note, this has full error handling and uses the nodejs calling convention for the callback that passes parameters (err, result):
function getConfigData(filename, callback) {
if (typeof callback === "function") {
fs.readFile(filename, function(err, data) {
try {
if (err) throw err;
let result = JSON.parse(data);
callback(null, result);
} catch(e) {
callback(err);
}
});
} else {
return fs.promises.readFile(filename).then(data => {
return JSON.parse(data);
}).
}
}
This could then be used as either:
getConfigData('./config.json').then(result => {
console.log(result);
}).catch(err => {
console.log(err);
});
configData('./config.json', (err, data) => {
if (err) {
console.log(err);
} else {
console.log(data);
}
});
Depending upon the specific asynchronous operation, it may be better or more efficient to have two separate implementations internally or to have one implementation that you adapt at the end to either a callback or a promise.
And, there's a useful helper function if you adapt a promise to a callback in multiple places like this:
function callbackHelper(p, callback) {
if (typeof callback === "function") {
// use nodejs calling convention for callbacks
p.then(result => {
callback(null, result);
}, err => {
callback(err);
});
} else {
return p;
}
}
That lets you work up a simpler shared implementation:
function getConfigData(filename, callback) {
let p = fs.promises.readFile(filename).then(data => {
return JSON.parse(data);
});
return callbackHelper(p, callback);
}
I want to use the async function to bring out a particular value from my database to my the function global so I can use it in other parts of my application.
async function dimension() {
const result = await Settings.find({_id : "5d7f77d620cf10054ded50bb"},{dimension:1}, (err, res) => {
if(err) throw new Error(err.message, null);
const holder = res[0].dimension;
return holder;
console.log(holder) /// this print the expected result, that i want to make global
});
return {
result
};
};
console.log(dimension())
but the console.log of the dimension() gives me this
Promise { <pending> }
instead of the same value that
console.log(holder)
gives me nothing.
The problem is you are printing the result of dimension() as soon as you call it, but since this function is async, it returns a promise that is not yet resolved.
You do not need to use async/await here. Settings.find() seems to return a Promise. You can just return directly this Promise and use .then() to do something once that promise is resolved.
Like this :
function dimension () {
return Settings.find({ _id: '5d7f77d620cf10054ded50bb' }, { dimension: 1 }, (err, res) => {
if (err) {
throw new Error(err.message, null);
}
return res[0].dimension;
});
}
dimension().then(result => {
//print the result of dimension()
console.log(result);
//if result is a number and you want to add it to other numbers
var newResult = result + 25 + 45
// the variable "newResult" is now equal to your result + 45 + 25
});
More info on Promises and async/await
You have to await for your result, like this:
const result = await dimension();
console.log(result);
In that case, you don't even to make the original function async, just write it like this:
function dimension() {
return Settings.find({_id : "5d7f77d620cf10054ded50bb"},{dimension:1}, (err, res) => {
if(err) throw new Error(err.message, null);
const holder = res[0].dimension;
return holder;
});
};
async function myGlobalFunc() {
const result = await dimension();
console.log(result);
}
The best way to have this globally available is to just put your function dimension in a file somewhere. Then where you need the value, you just require it and await its value. E.g.
// get-dimension.js
// ...const Settings = require... comes here
module.exports = function getDimension() {
return Settings.find({_id : "5d7f77d620cf10054ded50bb"},{dimension:1}, (err, res) => {
if(err) throw new Error(err.message, null);
const holder = res[0].dimension;
return holder;
});
}
// your other modules, e.g.
// my-service-handler.js
const getDimesion = require('./get-dimension');
async function myServiceHandler() {
const dimension = await getDimension();
// do stuff with dimension.
}
You're using async/await, but you're mixing it with callbacks, this is not desirable as it leads to confusion. It's not clear what you expect to happen in the callback, but return holder; likely doesn't do what you expect it to do, returning from a callback does not work the same way returning from a promise handler works. Your entire implementation should work with promises so that the async/await syntax reads more naturally (as it was intended).
async function dimension() {
// We're already awaiting the result, no need for a callback...
// If an error is thrown from Settings.find it is propagated to the caller,
// no need to catch and rethrow the error...
const res = await Settings.find({_id: "5d7f77d620cf10054ded50bb"}, {dimension: 1});
return {result: res[0].dimension};
}
(async () => {
try {
console.log(await dimension());
} catch (err) {
console.error(err);
}
})();
Use dimension().then() in your code then it will work fine.
async function globalDimension() {
const data = await Users.findOne({ phone: 8109522305 }).exec();
return data.name;
}
globalDimension().then(data => {
console.log(data);
});
I'm trying to learn Asynchronous programming with NodeJS and I'm having trouble understanding how to create usable functions.
I'm trying to compare the results of a HTTP get request and a file read all inside an "express" callback. What is the best way to split out two different async operations into their own functions so that they can be used again together in a different callback?
I Have it working when I write everything inside the express callback
app.get('/', (req, res) => {
axios.get('http://127.0.0.1:8080')
.then(function(response) {
var http_data = response.data
// Do more stuff with data
fs.readFile('fwversion_current', 'utf8', function(err, contents) {
var file_data = contents.trim()
// Do more stuff with data
if (http_data == file_data) {
res.send("Match")
}
else {
res.send("No Match")
}
});
});
But I'm hoping for something more like this so I can use these same operations in other places. I'm not sure the right node way to get there.
function getHttpData() {
axios.get('http://127.0.0.1:8080')
.then(function(response) {
var http_data = response.data
// Do more stuff with data
return http_data
});
}
function getFileData() {
fs.readFile('fwversion_current', 'utf8', function(err, contents) {
var file_data = contents.trim()
// Do more stuff with data
return file_data
});
}
app.get('/', (req, res) => {
let http_data = await getHttpData()
let file_data = await getFileData()
if (http_data == file_data) {
res.send("Match")
}
else {
res.send("No Match")
}
});
You will need to wrap those functions inside a function that returns a Promise, this will let you the ability to await for them to complete before continuing.
function getHttpData(url) {
// axios.get already returns a Promise so no need to wrap it
return axios.get(url)
.then(function(response) {
let http_data = response.data;
// Do more stuff with data
return http_data;
});
}
function getFileData(path) {
return new Promise(function(resolve, reject) {
fs.readFile(path, function(err, contents) {
if (err) {
reject(err);
return;
}
let file_data = contents.trim();
// Do more stuff with data
resolve(file_data);
});
});
}
Now when both functions returns a Promise we can await for them to complete.
Make the handler an async function because it's needed to use the await keyword, I'm using Promise.all to fire both requests simultaneously and not wait for one to complete before we fire the other.
Wrap it in a try catch to handle errors and send status 500
app.get('/', async (req, res) => {
try {
const [http_data, file_data] = await Promise.all([
getHttpData(url),
getFileData(path),
]);
http_data == file_data
? res.send('Match')
: res.send('No Match');
} catch (err) {
console.error(err);
res.status(500).send('Something went wrong');
}
});
I'm getting an error that says
(node:27301) UnhandledPromiseRejectionWarning: Unhandled promise rejection (rejection id: 1): Error: Callback was already called.
From what I understand about rejecting promises in await's and per the Mozilla description:
If the Promise is rejected, the await expression throws the rejected value.
I reject the error in the callback that's wrapped around my Promise like so:
Airport.nearbyAirports = async (location, cb) => {
let airports
try {
airports = await new Promise((resolve, reject) => {
Airport.find({
// code
}, (err, results) => {
if (err)
reject(err) // Reject here
else
resolve(results)
})
})
} catch (err) { // Catch here
cb(err, null)
return
}
if (!airports.empty)
cb(null, airports)
}
My question is
Why does it still consider my promise rejection unhandled? I thought the catch statement should silent this error.
Why does it consider my callback already called? I have a return statement in my catch, so both should never be called.
The problem was actually my framework (LoopbackJS), not my function. Apparently at the time of writing this, using promises are not supported:
https://loopback.io/doc/en/lb3/Using-promises.html#setup
Meaning I can't even use await in my function because the remote method wraps my function somewhere else, so async would always be unhandled. I ended up going back to a Promise-based implementation of the inner code:
Airport.nearbyAirports = (location, cb) => {
const settings = Airport.dataSource.settings
const db = DB(settings)
let airports
NAME_OF_QUERY().then((res) => {
cb(null, res)
}).catch((err) => {
cb(err, null)
})
If Airport.find() throws an exception, then execution will jump to your catch block and your Promise will never be resolved or rejected. Perhaps you need to wrap it in its own try/catch:
Airport.nearbyAirports = async (location, cb) => {
let airports
try {
airports = await new Promise((resolve, reject) => {
try {
Airport.find({
// code
}, (err, results) => {
if (err)
reject(err) // Reject here
else
resolve(results)
})
} catch (err) {
reject(err) // Reject here too
cb(err, null)
}
})
} catch (err) { // Catch here
cb(err, null)
return
}
if (!airports.empty)
cb(null, airports)
}
As said here, loopback 3 support this by allowing you to use a simple return.
This :
Entry.findFooById = async (id, cb) => {
const result = await Entry.findById(id);
return result;
};
...Is equivalent to :
Entry.findFooById = (id, cb) => {
Entry.findById(id)
.then(result => cb(null, result))
.catch(cb);
};
We use Loopback 2.31.0 and it also supports simple return for async functions used for remote methods. If you put a break-point somewhere in your remote method and jump one level above it in the call-stack you will see how it is implemented in loopback itself (shared-method.js):
// invoke
try {
var retval = method.apply(scope, formattedArgs);
if (retval && typeof retval.then === 'function') {
return retval.then(
function(args) {
if (returns.length === 1) args = [args];
var result = SharedMethod.toResult(returns, args);
debug('- %s - promise result %j', sharedMethod.name, result);
cb(null, result);
},
cb // error handler
);
}
return retval;
} catch (err) {
debug('error caught during the invocation of %s', this.name);
return cb(err);
}
};
What it does here - it calls your function and if it is an async function - it will return a promise (retval.then === 'function' will be true). In this case loopback will handle your result correctly, as a promise. It also do the error check for you, so you no longer try/catch blocks in your code anymore.
So, in your own code you just need to use it like below:
Airport.nearbyAirports = async (location) => {
let airports = await new Promise((resolve, reject) => {
Airport.find({
// code
}, (err, results) => {
if (err)
reject(err) // Reject here
else
resolve(results)
})
});
if (!airports.empty)
return airports;
}
else {
return {}; // not sure what you would like to return here as it wan not handled in your sample...
}
}
Note, you do not need to use callback (cb) at all here.
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.