Choose which data to use using promise/async - javascript

I tried the below to acces both data and json values, however I can now only acces the data values, what can I do to acces the json values as well?
const getUser = user => new Promise(async (resolve, reject) => {
try {
const read = await snekfetch.get('https://www.website.nl/api/public/users?name=' + user);
const data = JSON.parse(read.text);
const result = await snekfetch.get('https://www.website.com/api/public/users/' + data.uniqueId + '/profile');
const json = JSON.parse(result.text);
resolve(data, json);
} catch (error) {
reject(error);
}
});
const promise = Promise.resolve(getUser(args[0]));
promise.then(function(data, json) {
const name = data.name;
const motto = data.motto;
const memberSince = data.memberSince;
const groups = json.groups.length;
const badges = json.badges.length;
const friends = json.friends.length;
const rooms = json.rooms.length;
message.channel.send(`${name}\n${motto}\n${memberSince}\n${groups || 'N/A'}\n${badges || 'N/A'}\n${friends || 'N/A'}\n${rooms || 'N/A'}\n`);
}).catch(function(err) {
console.log(err);
return message.reply(`${args[0]} does not exists.`);
});

when you resolve a promise, you pass in a single value with the data you want to resolve it to. If there are multiple pieces of data you want to resolve with, then stick them in an object and resolve with that object
const getUser = user => new Promise(async (resolve, reject) => {
try {
const read = await snekfetch.get('https://www.website.nl/api/public/users?name=' + user);
const data = JSON.parse(read.text);
const result = await snekfetch.get('https://www.website.com/api/public/users/' + data.uniqueId + '/profile');
const json = JSON.parse(result.text);
resolve({ data, json }); // <--- created an object with two properties
} catch (error) {
reject(error);
}
});
getUser('someUser')
.then((result) => {
console.log(result.data)
console.log(result.json)
})
Additionally, i want to point out that you're creating extra promises where they are not needed. async functions automatically create promises, so your getUser function can just be:
const getUser = async (user) => {
const read = await snekfetch.get('https://www.website.nl/api/public/users?name=' + user);
const data = JSON.parse(read.text);
const result = await snekfetch.get('https://www.website.com/api/public/users/' + data.uniqueId + '/profile');
const json = JSON.parse(result.text);
return { data, json };
}

Related

React jsonserver promise result issue

I am creating a react/ redux app with json fake api server I am trying to add a login and trying to get data from json fake api server, data is showing and all ok , but data is always resulting as a promise and the required data is inside the promise. i tried many ways to distructure but throwing errors , could anyone help me on this,
my axios request
const urlss = "http://localhost:5000/users";
export const userslist = async () => {
const r = await axios.get(urlss);
const data = r.data;
return data;
};
const newout2 = userslist();
const newout = newout2;
console.log(newout);
the place where I am using it
export const login = (credentials) => (dispatch) => {
return new Promise((resolve, reject) => {
const matchingUser =
newout2 &&
newout2.find(({ username }) => username === credentials.username);
if (matchingUser) {
if (matchingUser.password === credentials.password) {
dispatch(setUser(matchingUser));
resolve(matchingUser);
} else {
dispatch(setUser(null));
reject("Password wrong");
}
} else {
dispatch(setUser(null));
reject("No user matching");
}
});
};
i am getting this error
You are using then in your userslist method while awaiting in an async method. drop the then and just use proper await inside an async method.
const urlss = "http://localhost:5000/users";
export const userslist = async () => {
const r = await axios.get(urlss);
const data = r.data;
return data;
};

How can I return document data Firebase database?

I'm learning React/Firebase and I have a function that queries parent node for a specific user.
export const fetchUserDocument = (userId) => {
const db = database.ref("users");
//if I write return here it outputs
// const user = snapshot.val();
// return user;
db.child(userId).on("value", (snapshot) => {
const user = snapshot.val();
return user;
});
//return db
};
Then in another file I can call on this function to retrieve user data like this const user = await fetchUserDocument(userId) but I am getting undefined when I console.log(user).
onSnapshot is asnynchronous, so it cannot return value to you immediately. You need to pass a callback function to fetchUserDocument and call it when you got the user data:
export const fetchUserDocument = (userId, cb) => {
const db = database.ref("users");
db.child(userId).on("value", (snapshot) => {
const user = snapshot.val();
cb(user);
});
};
fetchUserDocument(userId, (user) => {
console.log(user);
});
Another approach would be returning a promise from fetchUserDocument:
export const fetchUserDocument = async (userId) => {
return new Promise((resolve, reject) => {
const db = database.ref("users");
db.child(userId).on("value", (snapshot) => {
const user = snapshot.val();
resolve(user);
});
});
};
This would give you the result you want:
const user = await fetchUserDocument(userId);
console.log(user);

How to Test a Nested await function in JS

const test = async (url, id) => {
if (!isValidUrl(url)) {
throw new Error('Invalid URL')
}
const storage = new Storage(Indexdb, id);
const cae = new valueExtract(url);
const data = await cae.fetch();
const obj = await new ZIPExtractor(data); // shudder. A constructor should never return a promise
const zip = await obj.getZip();
const list = await zip.getList();
const sI = storage.connection;
await Promise.all(Object.keys(list).map(async (fileName, index) => {
const blob = await new FileExtractor(list[fileName]);
const store = new StoreObject(fileName, 'testData', blob);
await sI.setItemForce(fileName, store.dataObject);
}));
return sI; // or something?
}
This is the answer of a question i had asked , I wanted to also know how do we test such nested awaited functions.
unit Test of Normal Fun
test('Tes scenario', () => {
let storage = new StorageAdapter(
type,
dbName
)
expect(storage.storageName).toBe(dbName)
expect(storage.storageType).toEqual(type)
expect(storage.connection).not.toBeNull()
})

I get Promise { <pending> } as returned value and also calling in async scope gives me undefined immediately

Im trying to return a value from a Promise in async-await form and use it in another function in another file, but I do have problem because my Promise doesnt return any value.
When im trying to console.log('website') it returns me undefined immediately (it's like the value is not being fetched at all from API services). I dont know what im doing wrong, I really love to learn about Promises and Async-Await but each time im trying to work with them im getting more confused.
const dns = require('dns')
const iplocation = require("iplocation").default;
const emojiFlags = require('emoji-flags');
const getServerIPAddress = async (server) => {
return new Promise((resolve, reject) => {
dns.lookup(server, (err, address) => {
if (err) throw reject(err);
resolve(address);
});
});
};
const getServerLocation = async (server) => {
const ip = await getServerIPAddress(server)
iplocation(ip).then((res) => {
const country = emojiFlags.countryCode(res.countryCode)
const result = `Location: ${country.emoji} ${country.name}`
return result
})
.catch(err => {
return `Location: Unknown`
});
}
(async function() {
console.log(await getServerLocation('www.google.com'))
})()
module.exports = {
getServerLocation
}
It is really important for me to get result from this function first, then use its value in another function. I wish you could give me tips on how to do tasks asynchronously.
You're clearly using async so it's not apparent why you're using then as well. If you use then then you must return the promise as well in order to preserve the promise chain:
const getServerLocation = async (server) => {
const ip = await getServerIPAddress(server)
return iplocation(ip).then((res) => {
const country = emojiFlags.countryCode(res.countryCode)
const result = `Location: ${country.emoji} ${country.name}`
return result
})
.catch(err => {
return `Location: Unknown`
});
}
Otherwise just async this:
const getServerLocation = async (server) => {
const ip = await getServerIPAddress(server)
let res = await iplocation(ip);
const country = emojiFlags.countryCode(res.countryCode)
const result = `Location: ${country.emoji} ${country.name}`
return result
}
const getServerLocation = async (server) => {
const ip = await getServerIPAddress(server)
//you need to return
return iplocation(ip).then((res) => {
const country = emojiFlags.countryCode(res.countryCode)
const result = `Location: ${country.emoji} ${country.name}`
return result
})
.catch(err => {
return `Location: Unknown`
});
}

How to write an async function that resolves when `data` event emitter fires

I am using node-serialport to communicate with a piece of hardware. It just writes a command and receives a response.
https://serialport.io/docs/en/api-parsers-overview
The following code works:
const port = new SerialPort(path);
const parser = port.pipe(new Readline({ delimiter: '\r', encoding: 'ascii' }));
const requestArray = [];
parser.on('data', (data) => {
// get first item in array
const request = requestArray[0];
// remove first item
requestArray.shift();
// resolve promise
request.promise.resolve(data);
});
export const getFirmwareVersion = async () => {
let resolvePromise;
let rejectPromise;
const promise = new Promise((resolve, reject) => {
resolvePromise = resolve;
rejectPromise = reject;
});
const title = 'getFirmwareVersion';
const cmd = 'V\r';
requestArray.push({
title,
cmd,
promise: {
resolve: resolvePromise,
reject: rejectPromise
}
});
await v2Port.write(cmd);
return promise;
};
Then from my app (which is written in electron/react) I can call the function:
<Button onClick={() => {
let data = await _api.getFirmwareVersion();
console.log('done waiting...');
console.log(data);
}>
Click Me
</Button>
Is there anyway I can refactor this code to make it more succinct?
Is there a way to get the Promise from the async function, rather than having to make a new Promise?
Is there a way to tap into the Transform Stream that already exists and pipe the Promise in there somehow?
I'm also new to async/await, and wanted to avoid using callbacks, especially in the React/Redux side of things.
I aim to have a lot of these endpoints for the api (i.e. getFirmwareVersion, getTemperature, etc...). So I want to make the code as concise as possible. I don't want the UI to have any underlying knowledge of how the API is getting the data. It just needs to request it like any other API and wait for a response.
Oh, I think I get it. The parser is receiving data constantly. So when a request comes, you wait for the next data and send it when it arrives. I suggest you to write an intermediate class.
Like this:
const SerialPort = require('serialport')
const Readline = require('#serialport/parser-readline')
const { EventEmitter } = require('events');
class SerialPortListener extends EventEmitter {
constructor(path) {
super();
this.serialPortPath = path;
}
init() {
this.serialPort = new SerialPort(this.serialPortPath);
const parser = this.serialPort.pipe(new Readline({ delimiter: '\r', encoding: 'ascii' }));
parser.on('data', data => this.emit('data', data));
}
}
Then you could modify the getFirmwareVersion like this:
const serialPortListener = new SerialPortListener(path);
serialPortListener.init();
export const getFirmwareVersion = () => {
return new Promise((resolve, reject) => {
serialPortListener.once('data', async (data) => {
try {
const cmd = 'V\r';
await v2Port.write(cmd);
resolve(data);
} catch (ex) {
reject(ex);
}
});
});
};
Based on help from Mehmet, here is what I ended up with:
const _port = new SerialPort(path);
const _parser = _port.pipe(new Readline({ delimiter: '\r', encoding: 'ascii' }));
const waitForData = async () => {
return new Promise((resolve, reject) => {
const timeoutId = setTimeout(() => reject('Write Timeout'), 500);
_parser.once('data', (data) => {
clearTimeout(timeoutId);
resolve(data);
});
});
};
const createAPIFunction = (cmdTemplate, validationString) => {
return async (config) => {
try {
// replace {key} in template with config[key] props
const cmd = cmdTemplate.replace(/{(\w+)}/g, (_, key) => {
return config[key];
});
_port.write(cmd + '\r');
const data = await waitForData();
// validate data
if (data.startsWith(validationString)) {
// is valid
return data;
} else {
// invalid data
throw new Error('Invalid Data Returned');
}
} catch (err) {
throw err;
}
};
};
export const getFirmwareVersion = createAPIFunction('V', 'V1');
export const enableSampling = createAPIFunction('G1{scope}', 'G11');

Categories