How to retrieve object from JSON in nodejs? - javascript

Having this code:
const fs = require('fs')
const file = 'books.json';
class Book{
constructor(code) {
this._code = code;
}
get code() {
return this._code;
}
set code(value) {
this._code = value;
}
}
async function writeBooks(){
const data = JSON.stringify([new Book('c1'), new Book('c2')]);
await fs.promises.writeFile(file, data, 'utf8');
}
async function getBook(code){
try{
const data = await fs.promises.readFile(file);
const array = JSON.parse(data);
return array.find(b => b.code === code);
} catch (err){
console.log(err)
}
}
writeBooks();
getBook('c1').then(b => console.log(b));
I am getting undefined (instead of the expecting book object).
How to get the object (the above problem)
If async function always returns promise, how can I then return object for the client, instead of him having to call then() from the getBook(code)?
do I need to await for the fs.promises.writeFile()? as I am doing in writeBooks()? As fas as I understand the async/await now, is that the return value from await function is the data or error. But since the writeFile() does not returns anything, or error at most (as opposed to readFile()) why would I want to await for no data?

Actually the root of problem is not about async/awaits or promises. The problem is trying to write an array to a json file. If you write your json data like the code snippet below (as a key-value pair), your problem is solved.
{"1": [new Book('c1').code, new Book('c2').code]} //as a key-value pair
const fs = require('fs')
const file = 'books.json';
class Book{
constructor(code) {
this._code = code;
}
get code() {
return this._code;
}
set code(value) {
this._code = value;
}
}
async function writeBooks(){
const data = JSON.stringify({"1": [new Book('c1').code, new Book('c2').code]});
await fs.promises.writeFile(file, data, 'utf8');
}
async function getBook(code){
try{
const data = await fs.promises.readFile(file);
const dataOnJsonFile = JSON.parse(data);
return dataOnJsonFile["1"];
} catch (err){
console.log(err)
}
}
writeBooks();
getBook('c1').then(b => console.log(b));

The above problem is that the Books returned from JSON.parse have only data, not methods, and thus I cannot get the code via get code(){}, but only as public parameter of class Book as book._code, which however breaks encapsulation (convetion is that _[propery] is private, and there should be appropriate getters/setters). So I made the properties public (and broke encapsulation), because I still don't know, how to assign methods to object created from JSON.
No, the result of async is always Promise. You cannot unwrap it inside async, the client will always have to unwrap it. (so await fs.promises.WriteFile() will unwrap it, but then immediately wrap it back, before async function returns.
as explained above.

Related

How to consume a promise and use the result later on with my code?

I'm new to async operations and js. Here is my question.
I have a Person class. I want to init Person instance using data that I get from an API call.
class Person {
constructor(data) {
this.data = data;
}
}
I am making an API call using Axios. I get a response and want to use it in my class.
const res = axios.get('https://findperson.com/api/david');
const david = new Person(res);
I understand that res is a promise at this stage, and that I need to consume it.
How should I do it?
How can I take the response and use it properly?
axios.get() return a promise of an object which contains the returned data, status, headers, etc...
async function getPerson() {
try {
const res = await axios.get('https://findperson.com/api/david');
const david = new Person(res.data);
// do something with david
} catch (error) {
console.log(error)
}
}
or
function getPerson() {
axios
.get('https://findperson.com/api/david')
.then(res => {
const david = new Person(res.data)
// do something with david
})
.catch(console.log)
}
Inside another async function, or at the top level of a module or at the REPL (in node 16.6+ or some earlier versions with the --experimental-repl-await feature enabled), you can just use await.
const res = await axios.get('https://findperson.com/api/david');
That will wait for the promise to be resolved and unwrap it to store the contained value in res.
If you want to get the value out of the world of async and into synchro-land, you have to do something with it via a callback function:
axios.get('https://findperson.com/api/david').then(
res => {
// do stuff with res here
});
... but don't be fooled; without an await, any code that comes after that axios.get call will run immediately without waiting for the callback. So you can't do something like copy res to a global var in the callback and then expect it to be set in subsequent code; it has to be callbacks all the way down.
You can do this:
axios.get('https://findperson.com/api/david').then(res => {
const david = new Person(res);
});
Or in async function: (See async await for javascript)
const res = await axios.get('https://findperson.com/api/david');
const david = new Person(res);

async-await with console.log works but when I try to use the return statement I'm getting a promise back instead?

I'm hoping somebody can point out the error of my ways here.
I have two functions at the moment. One is getData and it's an async function that simply makes an API call requesting data. The second function is getRandomCategories that encapsulates the getData function call and holds the value of that async operation in a variable called res. The rest of the code within getRandomCategories manipulates the response data to an array of numbers where each number represents a category.
When I use the debugger statement in the getRandomCategories function (right before the return statement within the try block) I'm getting the data type I'm expecting from my variable named apiCallCategoryArray - it's an array of numbers each representing a category. Life is good.
Here's what I'm not understanding: If you look at the two function calls towards the bottom of the snippet (test() and testAgain() they're essentially the same functions but just with different names so I can compare them side-by-side) I'm able to console.log(res) in test() with the expected return value (an array of numbers) but when I try to return res (in testAgain()) the variable myData2 does NOT contain an array of numbers (as what I'm expecting it to contain) instead it contains: Promise { <state>: "pending" }
Here's my code below:
async function getData(endpoint, query, value) {
return await axios.get(
`http://jservice.io/api/${endpoint}?&${query}=${value}`
)
}
// createa a function that will return 6 random categories
async function getRandomCategories(data) {
try {
const res = await getData('categories', 'count', 50)
const data = res.data;
const categories = filterCategoryData(data); // I'm filtering for categories
const categoryIdArr = mapCategoryIds(categories); // an array of just category Ids
const shuffledCategoryIds = shuffle(categoryIdArr); // shuffles the array
const apiCallCategoryArray = takeFirstXItems(shuffledCategoryIds, 6); // takes 6 items
debugger // values are as expected
return apiCallCategoryArray
} catch (err) {
console.log(err);
}
}
// this logs what my expected return value should be
async function test() {
const res = await getRandomCategories()
console.log(res) // Array(6) [ 11518, 11537, 11533, 11512, 11542, 11506 ]
}
// But when I try to use the return statement so I can use the array
// I'm getting a promise back. This NOT my expected return value??
async function testAgain() {
const res = await getRandomCategories()
return res // return statement instead of console.log()
}
const myData = test() // the console.log within the test function logs the expected data type
console.log(myData) // but if I console.log myData it logs: Promise { <state>: "pending" }
const myData2 = testAgain() // same function but just a different name but this time I'm using a return statement instead of console.log
console.log(myData2) // Promise { <state>: "pending" }
My question is: why am I able to console.log(res) with the expected return value in test()
but when I use the return statement (like in testAgain()) I'm getting a Promise { : "pending" } instead of the array of numbers like I'm expecting. Where do I tweak my code so that my variable holds an array of numbers instead of the Promise?
Thanks in advance for everyone's time.
const data = res.data; should be const data = await res.data; in the getRandomCategories function. Also const myData2 = testAgain() should be const myData2 = await testAgain() but as it is top level code and await needs an async function, you could wrap it into an self-invoking function or use then/catch.
More info about async/await here.

How do I call a JavaScript async function and get a result object, not promise, from it?

I have some JavaScript code run in browser where I have a very complex object and one of its fields contains a string which I need to replace with a hash of that string. There's SHA-512 implementation built in, but I cannot figure out how to have it invoked synchronously such that execution pauses and waits for the result. I tried this (adapted from here):
digestMessage = async (dataString) => {
let encoder = new TextEncoder();
let data = encoder.encode(dataString);
const hash = await crypto.subtle.digest('SHA-512', data);
return hash;
};
digest = async (data) => {
try {
const result = await digestMessage(data);
return result;
} catch (error) {
throw error;
}
};
Invocation:
myObject.fieldToTransform = digest(myObject.fieldToTransform);
//continue with the object
And... sure, it gets me a yet another promise object. Rewriting the invoking code with .then() would complicate it greatly. I want the hash, not the promise, so that I pass store that hash and pass it further.
How do I have the hash computed, not promised?

getting data from js promise

I am doing an api call that returns a promise. The call works fine but I want to do treat data contained within the promise. Here is My call:
let promiseArray = this.get('store').query('member', {query: term, option: this.get('option')});
promiseArray.then(members => {console.log(members);
});
let var= members;
console.log(var);
My problem is that this doesn't return an array of my model i.e members, also the second display of members display undefined , it return an object containing a lot of meta data, also the array but inside some meta data.
How could I get simply the array ?
You can use async await for your purpose.
const promiseFunc = () => {
// Return the promise and await this inside a async function
return this.get('store').query('member', {query: term, option: this.get('option')});
}
const asyncFunc = async () => {
const value = await promiseFunc();
console.log(value);
}
asyncFunc();

Extracting JSON from Fetch API

I realise very similar questions have been answered before, but I'm still finding it very confusing as to how this works...
From my understanding promises are used to deal with asyc requests - these promises essentially send back the state or a "promise" that at some point later a JSON body (or other object) will be delivered.
What I'm trying to understand is how I properly handle these requests so that the function doesn't return until the JSON body is ready to be parsed.
Below I'm trying to simply extract the key "result" (which returns a string "result") and parse it to another variable that can be stored and then later used somewhere else in my code. Unfortunately, my code always returns a [Object Promise], rather than the extracted JSON. I believe this is because response.json is also a promise... however, I don't understand how I get out of the "chain of promises" and return a value that I can actually do something with.
Thanks for any advice,
async function name() {
const response = await fetch('https://xxxxx.herokuapp.com/timespent', {});
const json = await response.json();
return json.result;
}
let varr = name();
console.log(varr)
Since your function is async it always return a promise. You need to use await for result.
read more about async here
async function name() {
const response = await fetch('https://mautargets.herokuapp.com/timespent', {});
const json = await response.json();
return json.result;
}
async function result(){
//await can only be called from inside of async function. So we need async function for await name()
let varr = await name();
console.log(varr) // Success
}
result()
In your example code, name function is declared async, so it returns a promise.
Inside that function body, you correctly handle async calls like fetch or the JSON transformation.
What you need now is either use await to wait for the function to "resolve", or use the "older" then/catch promises methods. Note that you cannot always use await outside an async function so you may need to wrap it.
Example :
async function name() {
const response = await fetch('https://mautargets.herokuapp.com/timespent', {});
const json = await response.json();
return json.result;
}
// using promise.then
name().then(result => console.log(result));
// wrapping await
(async function test() {
try{
console.log(await name());
}catch(error) {
// error goes here if promise got rejected
}
})()
You could have a callback in the function declaration, and use '.then(...)' and call it when the promise has been resolved:
async function name(cb) {
const response = await
fetch('https://mautargets.herokuapp.com/timespent', {});
const json = response.json();
json.then(x => cb(x))
}
name(console.log)
This is because you're using an Async function, which will return a promise.
Or if you would like the method to return, you could either call it in another Asynchronous context and utilize await again:
// Assume no callback: code just as you had it.
async function wrapper() {
console.log(await name())
}
Or you could do name().then(...) as specified before:
// Assume no callback: code just as you had it.
name().then(console.log)
Hope this helps!
I'm actually looking for the answer(same as yours), so I found this way.
I. ASK/REQUEST for data
async function fetchMyJson() {
const response = await fetch('https://1stAPI.devdeveloper1.repl.co/fiveD');
const myData = await response.json();
return myData;
}
II.GET Extract data
fetchMyJson().then(myData => {
let myData_output = myData.USD[0].rate; // fetched or Get OUTPUT data
console.log(myData_output);
document.body.innerHTML = `<div>${myData_output}</div>`; //make sure you add ${} for output
});
async function fetchMyJson() {
const response = await fetch('https://1stAPI.devdeveloper1.repl.co/fiveD');
const myData = await response.json();
return myData;
}
//GET Extract data
fetchMyJson().then(myData => {
let myData_output = myData.USD[0].rate; // fetched or Get OUTPUT data
console.log(myData_output);
document.body.innerHTML = `<div>${myData_output}</div>`; //make sure you add ${} for output
});
It is correct that you await fetch and .json since they are async.
async function name() {
const response = await fetch('http://blah.com/api', {});
const json = await response.json();
return json.result;
}
However, async and Promises inside function name make it async too. So the return value of name is a Promise that you should await it, or .then it, like:
// Old style .then
name().then(result => console.log(result))
// Modern style await
async function main() {
const result = await name()
console.log(result)
}

Categories