Please, help me
I have a script
export function GetKey(inn, res) {
try {
const body = {
7709798583: {
name: 'someName',
key: '123'
},
7718266352: {
name: 'otherName',
key: '123'
}
};
res(body[inn]['key']);
} catch (err) {
res('0000000000000');
}
};
In other file I try to use this function
GetKey(param, (name) => {
console.log(name);
});
It's ok. but I need to return callback to the parametr. How?
var param = GetKey(param, (name) => {
return name;
});
is not correct and return undefined
That's not how callbacks work; although, you can fake that behavior using Promise and async-await syntax.
If you want to write your code like you have it, you'll want to put the rest of your logic in a function and pass it directly to your callback:
var param = ''
var allYourLogic = name => {
// YOUR LOGIC
param = name
}
GetKey(param, allYourLogic);
Or you can simply inline your logic:
GetKey(param, (name) => {
param = name
// YOUR LOGIC
});
Using the Promise syntax, this is how it looks:
new Promise(resolve => {
GetKey(param, resolve)
})
.then(name => {
param = name
// YOUR LOGIC
})
Lastly, using the async-await methodology:
var param = (
await new Promise(resolve => {
GetKey(param, resolve)
})
)
Really though, it seems like you're doing something wonky which is why you're running into this issue in the first place.
Your entire application will act like it's asynchronous as soon as you use a callback because the callback doesn't execute immediately in Node.js's event loop. Instead, the current function you're in will finish executing before the GetKey function calls the callback method.
Related
I am developing a server project which needs to call some functions synchronously. Currently I am calling it in asynchronous nature. I found some similar questions on StackOverflow and I can't understand how to apply those solutions to my code. Yet I tried using async/await and ended up with an error The 'await' operator can only be used in an 'async' function
Here is my implementation
function findSuitableRoom(_lecturer, _sessionDay, _sessionTime, _sessionDuration, _sessionType){
let assignedRoom = selectRoomByLevel(preferredRooms, lecturer.level, _sessionDuration); <------- Need to be call synchronously
if (!assignedRoom.success){
let rooms = getRooms(_sessionType); <------- Need to be call synchronously
assignedRoom = assignRoom(_lecturer.rooms, _sessionDuration, _lecturer.level);
} else {
arr_RemovedSessions.push(assignedRoom.removed)
}
return assignedRoom;
}
function getRooms(type){
switch (type){
case 'Tutorial' : type = 'Lecture hall'
break;
case 'Practical' : type = 'Lab'
break;
default : type = 'Lecture hall'
}
Rooms.find({type : type},
(err, rooms) => {
if (!err){
console.log('retrieved rooms ' + rooms)
return rooms;
}
})
}
Here I have provided only two methods because full implementation is very long and I feel if I could understand how to apply synchronous way to one method, I can manage the rest of the methods. Can someone please help me?
Well yes await is only available inside an async function so put async infront of findSuitableRoom.
Also you did a classic mistake. You use return inside of a callback function, and expect getRooms to return you some value.
async function findSuitableRoom(
_lecturer,
_sessionDay,
_sessionTime,
_sessionDuration,
_sessionType
) {
let assignedRoom = selectRoomByLevel(
preferredRooms,
lecturer.level,
_sessionDuration
);
if (!assignedRoom.success) {
try {
let rooms = await getRooms(_sessionType);
} catch (err) {
console.log("no rooms found");
}
assignedRoom = assignRoom(
_lecturer.rooms,
_sessionDuration,
_lecturer.level
);
} else {
arr_RemovedSessions.push(assignedRoom.removed);
}
return assignedRoom;
}
Also wrap it in an try / catch
Since .find() returns an promise if you dont pass an callback you can write it like this
function getRooms(type) {
switch (type) {
case "Tutorial":
type = "Lecture hall";
break;
case "Practical":
type = "Lab";
break;
default:
type = "Lecture hall";
}
return Rooms.find({ type });
}
Note here findSuitableRoom is no longer synchronouse. Its async and returns an promise. That means you will need to use the function like this:
findSuitableRoom.then(res => { console.log(res); })
The 'await' operator can only be used in an 'async' function
This means whenever you want to use the await keyword it needs to be inside a function which has an async keyword (returns promise)
read this https://javascript.info/async-await for more info
const add = (a, b) => {
return new Promise((resolve, reject) => {
setTimeout(() => { //to make it asynchronous
if (a < 0 || b < 0) {
return reject("don't need negative");
}
resolve(a + b);
}, 2000);
});
};
const jatin = async () => {
try{
const sum = await add(10, 5);
const sum2 = await add(sum, -100);
const sum3 = await add(sum2, 1000);
return sum3;
} catch (e) {
console.log(e);
}
};
jatin()
Try this
let's understand this
add is a normal function that does some asynchronous action like
waiting for 2 seconds
normally we use await with async function so in order to use it we make as async function jatin and use await with add function call
to make it synchronous, so until first await add call() doesn't
happen it wont execute another await add call().
Example code if you will use in your app.js
router.post("/users/login", async (req, res) => {
try {
const user = await User.findByCredentials(
req.body.email,
req.body.password
);
const token = await user.generateToken();
res.status(200).send({
user,
token,
});
}
catch (error) {
console.log(error);
res.status(400).send();
}
});
I'm using node-cache on my Node.JS & Express web service. But after a while, I've got a bunch of similar logics cluttering, like these:
let data = this.cache.get('some-key')
if (data) {
return shopInfo
} else {
data = someModel.find(id)
this.cache.set('some-key', data)
return data
}
I Googled about it and found a possible solution here. But I don't wanna pass a callback function every time. Is there a better way? Or how can I modify it to utilize async/await instead of callback function?
get(key, storeFunction) {
const value = this.cache.get(key);
if (value) {
return Promise.resolve(value);
}
return storeFunction().then((result) => {
this.cache.set(key, result);
return result;
});
}
You're going to need a callback or promise as a parameter. Here's perhaps an easier way:
Helper function:
get(key, retrieveData) {
const value = this.cache.get(key);
if (value) {
return value;
}
const data = retrieveData()
this.cache.set(key, data);
return data;
}
Then you can use:
const result = get('some-key', () => someModel.find(id))
return result
Despite using a callback, it's still clean. No need to complicate your code with promises if you don't need them.
if you dont want to past callback every time, you can do opposite - modify callback itself.
// simple memoized...
function memoized(cache, fn) {
return async key => cache.has(key) ? cache.get(key) : cache.set(key, await fn(key)).get(key);
}
// some model handler...
function fetchFunction(key) {
console.log('call fetchFunction');
return Promise.resolve(key + '_value');
}
// modify callback...
// example: model.fetchFunction = memoized(new Map(), model.fetchFunction).bind(model);
fetchFunction = memoized(new Map(), fetchFunction);
// test...
(async () => {
console.log(await fetchFunction('test'));
console.log(await fetchFunction('test'));
})()
I would like to have a configuration file with variables set with data I fetch from an API.
I think I must use async and await features to do so, otherwise my variable would stay undefined.
But I don't know how to integrate this and keep the node exports.myVariable = myData available within an async function ?
Below is the code I tried to write to do so (all in the same file) :
const fetchAPI = function(jsonQuery) {
return new Promise(function (resolve, reject) {
var reqOptions = {
headers: apiHeaders,
json:jsonQuery,
}
request.post(apiURL, function (error, res, body) {
if (!error && res.statusCode == 200) {
resolve(body);
} else {
reject(error);
}
});
});
}
var wallsData = {}
const fetchWalls = async function (){
var jsonQuery = [{ "recordType": "page","query": "pageTemplate = 1011"}]
let body = await utils.fetchAPI(jsonQuery)
let pageList = await body[0].dataHashes
for(i=0;i<pageList.length;i++){
var page = pageList[i]
wallsData[page.title.fr] = [page.difficultyList,page.wallType]
}
return wallsData
throw new Error("WOOPS")
}
try{
const wallsData = fetchWalls()
console.log(wallsData)
exports.wallsData = wallsData
}catch(err){
console.log(err)
}
The output of console.log(wallsData) shows Promise { <pending> }, therefore it is not resolved and the configuration file keep being executed without the data in wallsData...
What do I miss ?
Thanks,
Cheers
A promise is a special object that either succeeds with a result or fails with a rejection. The async-await-syntax is syntactic sugar to help to deal with promises.
If you define a function as aync it always will return a promise.
Even a function like that reads like
const foo = async() => {
return "hello";
}
returns a promise of a string, not only a string. And you need to wait until it's been resolved or rejected.
It's analogue to:
const foo = async() => {
return Promise.resolve("Hello");
}
or:
const foo = async() => {
return new Promise(resolve => resolve("Hello"));
}
Your fetchWalls similarly is a promise that will remain pending for a time. You'll have to make sure it either succeeds or fails by setting up the then or catch handlers in your outer scope:
fetchWalls()
.then(console.log)
.catch(console.error);
The outer scope is never async, so you cannot use await there. You can only use await inside other async functions.
I would also not use your try-catch for that outer scope promise handling. I think you are confusing the try-catch approach that is intended to be used within async functions, as there it helps to avoid nesting and reads like synchronous code:
E.g. you could do inside your fetchWalls defintion:
const fetchWalls = async function (){
var jsonQuery = [{ "recordType": "page","query": "pageTemplate = 1011"}]
try {
let body = await utils.fetchAPI(jsonQuery)
} catch(e) {
// e is the reason of the promise rejection if you want to decide what to do based on it. If you would not catch it, the rejection would chain through to the first error handler.
}
...
}
Can you change the statements like,
try{
const wallsData = fetchWalls();
wallsData.then((result) => {
console.log(result);
});
exports.wallsData = wallsData; // when importing in other file this returns as promise and we should use async/await to handle this.
}catch(err){
console.log(err)
}
I'm creating a utilities class to handle some of our common functions to help reduce code copy/past in our modules: I created an exports module. All that's happening here is the export of an object which contains three functions.
module.exports = {
//retrieve a secret object from the AWS Secrets Manager
//Parameter should be string value of the Secret ID
getSecretIdAsync : async (param) => {
return await new Promise((resolve, reject) => {
scrtmgr.getSecretValue({SecretId: param}, (err, data) => {
if(err){
reject(console.log('Error getting SecretId: ' + err, err.stack));
} else{
if('SecretString' in data)
return resolve(JSON.parse(data.SecretString));
}
});
});
},
//retrieves the AWS Paramter value from the AWS Paramter store
//param should be string value of parameter hierarchical id
getParameterValueFromStoreAsync : async (param) => {
return await new Promise((resolve, reject) => {
servmgr.getParameter({ Name: param}, (err, data) => {
if(err){
reject(console.log('Error getting parameter: ' + err, err.stack));
}
return resolve(data.Parameters.Value);
});
});
},
//retrieves the AWS Paramter "object" from the AWS Paramter store
//param should be string value of parameter hierarchical id
getParameterFromStoreAsync : async (param) => {
return await new Promise((resolve, reject) => {
servmgr.getParameter({ Name: param}, (err, data) => {
if(err){
reject(console.log('Error getting parameter: ' + err, err.stack));
}
return resolve(data.Parameters);
});
});
}
}
Whenever I attempt to reference this module (say in my unit test I reference to module as:
let chai = require('chai');
let ut = require('../utilities.js');
let expect = chai.expect;
let aws = require('aws-sdk-mock');
describe('get parameter value', ()=>{
it('resolves', (done)=>{
var result = aws.mock('SSM', 'putParameter' , {"Name": "someName", "Value": "someValue"} );
console.log('###### ' + JSON.stringify(ut));
//console.log(obj);
});
});
directory structure is
utilities.js is located in the root, where the unit test is in a folder called test.
Whenever I try to import the utilities module the object it always empty.
console.log('###### ' + JSON.stringify(ut)); generates ###### {}
I've exported individual functions in the past and I thought a group of functions would just require exporting a constructor.
Should multiple functions be exported in a different manner?
MDN web docs offers an explanation for the behavior that you're seeing with JSON.strigify():
If undefined, a Function, or a Symbol is encountered during conversion it is either omitted (when it is found in an object) or censored to null (when it is found in an array).
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/JSON/stringify
edit: To clarify, I think you're exporting your utility functions correctly, but JSON.stringify() explicitly promises to not print functions.
JSON.stringify doesn't serialize functions, if you need to do so you have to use a custom stringify function like this https://github.com/braceslab/json-stringify-extended
so, running
console.log(ut)
will print
{ getSecretIdAsync: [AsyncFunction: getSecretIdAsync],
getParameterValueFromStoreAsync: [AsyncFunction: getParameterValueFromStoreAsync],
getParameterFromStoreAsync: [AsyncFunction: getParameterFromStoreAsync] }
also, you can remove async and await from your functions, are useless using return new Promise
util.promisify appears to always expect 2 parameters from a callback function, however the existing functions do not have a seperate callback for err and data, but only a single callback.
How can I handle this ???
const {promisify} = require('util');
function y(text,cb){
setTimeout(function(){cb({error:false,data:text})},1000);
}
async function test(text) {
try{
const z = promisify(y)
return await z(text);
} catch(e) {return {error:true,msg:e}}
}
console.log(test('xxx'));
What I am looking for is to return the value from function y syncronously and not getting a promise i.e.
var x = test('xxx');
Given the information in your comment, you can wrap the function with a compatible signature to be passed directly to promisify():
const { promisify } = require('util')
function y (query, callback) {
callback(query)
}
function yCompatible (query, callback) {
y(query, ({ error, data }) => {
callback(error && data, error || data)
})
}
const yAsync = promisify(yCompatible)
async function test (query) {
try {
return yAsync(query)
} catch (error) {
return error
}
}
test('xxx').then(
data => { console.log(data) },
error => { console.error(error) }
)
Also try not to get in the habit of using single letter variables like a mathematician ;) I realize this is just example code, but even then it's helpful to be a bit more explicit about your intent.