NodeJS Promises returning Pending [duplicate] - javascript

This question already has answers here:
How do I return the response from an asynchronous call?
(41 answers)
Closed 1 year ago.
Context: I am trying to set up testing using jest and supertest for a MongoDB application I am writing
The aim: Assign a value returned from the postArticleByAPI function to a constant id.
Issue: It's returning Promise { <pending> }
What I have tried:
Promise.resolve(postArticleByAPI) results in the same issue.
Chaining a .then((res) => {console.log(res}) results in undefined.
I think I fundamentally don't understand promises, or namely how to assign values they return outside of the promise. Is this possible? Anyone have any tips?
const articleData = {title: 'Hello123', doi: '123', journal: 'Facebook'};
/**
* Posts an article through the API
* #param {Object} article - the article objection containing dummy data
* #return {string} request - the article id stored in the database
**/
async function postArticleByAPI(article) {
await request(app)
.post('/api/articles')
.send(article)
.expect(200)
.then((response) => {
expect(response.body.title).toBe(article.title);
expect(response.body.doi).toBe(article.doi);
expect(response.body.journal).toBe(article.journal);
expect(response.body.id).toBeTruthy();
return response.body.id;
});
}
describe('Test POST through API', () => {
test('It should response the POST method /api/articles', () => {
const id = postArticleByAPI(articleData);
console.log(id);
});
});

Indeed postArticleByAPI returns a Promise and it's not resolved at the time you log it. You should write this instead:
describe('Test POST through API', () => {
test('It should response the POST method /api/articles', async () => {
const id = await postArticleByAPI(articleData);
console.log(id);
});
});
Also don't forget to return the Promise from postArticleByAPI:
function postArticleByAPI(article) {
return request(app)
.post('/api/articles')
.send(article)
.expect(200)
.then((response) => {
expect(response.body.title).toBe(article.title);
expect(response.body.doi).toBe(article.doi);
expect(response.body.journal).toBe(article.journal);
expect(response.body.id).toBeTruthy();
return response.body.id;
});
}
If you want to use async and await, you should not use .then -
async function postArticleByAPI(article) {
const response =
await request(app)
.post('/api/articles')
.send(article)
.expect(200)
expect(response.body.title).toBe(article.title);
expect(response.body.doi).toBe(article.doi);
expect(response.body.journal).toBe(article.journal);
expect(response.body.id).toBeTruthy();
return response.body.id;
}

Related

Variable is not storing Promise result in Node [duplicate]

This question already has answers here:
Why is my variable unaltered after I modify it inside of a function? - Asynchronous code reference
(7 answers)
How do I access previous promise results in a .then() chain?
(17 answers)
Closed 2 years ago.
I'm developing a script that connects with an API, then with the JSON reponse do some operations and then reformat the JSON to send it to another API.
But now I'm stuck in the first step as I can't deal with the first part as my Promises is not working as expected. How can I store the API's response into a variable? For development puropose I stored one API response into a JSON file. This is my code:
declare var require: any;
let url = './data/big buy/big-bui-product-list.json';
const fs = require('fs');
let counter = 0;
const getProductList = () => {
return new Promise((resolve, reject) => {
fs.readFile(url, 'utf8', (err, data) => {
if(err){
return reject (err);
}
else {
return resolve(JSON.parse(data));
}
})
})
}
const getProductStock = () => {
return new Promise((resolve, reject) => {
fs.readFile('./data/big buy/big-bui-product-stock.json', 'utf8', (err, data) => {
if(err) {
return reject(err);
}
else {
return resolve(JSON.parse(data));
}
})
})
}
try {
let products;
console.log('Products:');
Promise.all([getProductList()])
.then(function(result) {
products = result[0];
});
console.log('Stocks:');
const productStock = Promise.all([getProductStock()]);
console.log(products);
}
catch(e) {
console.log((`Ha ocurrido un error: ${e.message}`));
}
finally {
}
In this code, what I do is getting a list of products and then get the stocks of all the products, later I will add a new function that will filter by stock and get me just a list of products where stock is bigger than X units. Now when I launch it from the terminal I dont' get the response stored into products variable but if I add .then((data) => console.log(data)) into the Promise I see on screen the JSON but as I dont' have it stored it in any variable I don't see how can I work with the objects I'm retrieving.
Promises are asynchronous. They are quite literally promises that a value will be yielded in the future. When you do getProductList().then(x => products = x), you're saying that Javascript should fetch the product list in the background, and once it's finished, it should set the products variable to x. The key words there are "in the background", since the code afterwards keeps running while the product list is being fetched. The products variable is only guaranteed to be set after the .then portion is run. So, you can move things into the .then:
try {
let products;
getProductList().then(list => {
products = list;
return getProductStock(); // leverage promise chaining
}).then(stocks => {
console.log("Products:", products);
console.log("Stocks:", stocks);
});
}
catch(e) {
console.log((`Ha ocurrido un error: ${e.message}`));
}
finally {
}
You seem to be missing some fundamental knowledge about promises. I suggest reading through the MDN Using Promises guide to familiarize yourself a bit with them.
A structure like below never works.
let variable;
promise.then(data => variable = data);
console.log(variable);
This is because it is executed in the following manner.
Create the variable variable.
Add a promise handler to the promise.
Log variable.
Promise gets resolved.
Execute the promise handler.
Set variable variable to data.
If you are using Node.js 10 or higher you can use the promise API of file system to simplify your code quite a bit.
const fs = require('fs/promises');
const readJSON = (path) => fs.readFile(path, "utf8").then((json) => JSON.parse(json));
const getProductList = () => readJSON("./data/big buy/big-bui-product-list.json");
const getProductStock = () => readJSON("./data/big buy/big-bui-product-stock.json");
Promise.all([
getProductList(),
getProductStock(),
]).then(([products, stocks]) => {
console.log({ products, stocks });
}).catch((error) => {
console.log("Ha ocurrido un error:", error.message);
}).finally(() => {
// ...
});

How to ensure a promise has resolved [duplicate]

This question already has answers here:
How do I return the response from an asynchronous call?
(41 answers)
Closed 2 years ago.
How do I ensure a promise has resolved in my configuration file. For example my configuration looks like the below.
const fetch = require("node-fetch");
const fetchToken = async () => {
return await fetch('www.token-endpoint.com', {
method: "POST",
headers: {
ContentType: "application/x-www-form-urlencoded",
},
body: new URLSearchParams({
secret: "this is a secret",
})
})
}
const ACCESS_TOKEN = fetchToken()
.then((res) => { return res.json() })
.then(...)
// And so on
// Now I want my promise to definitely resolve
module.exports = {
accessToken: ACCESS_TOKEN // This will be undefined unless my promise has resolved
}
After this I would like to use the token.
const config = require("./config")
// Use config.accessToken knowing it is well defined.
you can do something like this ,node fetch actually has
a built in function for converting the response to JSON, but it does not do it automatically in the same way that Axios and SuperAgent do. recent versions of this library use promises, so we're able to use async/await syntax with it as well:
const fetch = require('node-fetch');
(async () => {
try {
const response = await fetch('www.token-endpoint.com')
const json = await response.json()
console.log(json.url);
console.log(json.explanation);
} catch (error) {
console.log(error.response.body);
}
})();

Returning data from a Fetch api to build a UI [duplicate]

This question already has answers here:
How do I return the response from an asynchronous call?
(41 answers)
Closed 2 years ago.
First time using Stack Overflow, sorry if I do something wrong.
I'm building a simple app that allows you to enter a characters name from Rick and Morty, and then build a small card that displays that characters name, a pic of the char, their status, and their current location.
I currently can get the input from the input field and send that data to the API no problem. my issue is getting the data back from the API and setting it to a variable so I can then extract each individual piece of info from the API. i.e. character.name , character.location, etc...
Here's my JavaScript so far:
// UI variables
const input = document.getElementById("searchChar");
// Event Listeners
input.addEventListener("keyup", (e) => {
const userText = e.target.value;
if (userText !== "") {
let response = fetch(
`https://rickandmortyapi.com/api/character/?name=${userText}`
)
.then((res) => res.json())
.then((data) => console.log(data));
}
});
I can't return the 'data' or set it to a variable from the .then(data), so how do I get it to use it elsewhere in my code?
Any help is appreciated. I'm new to the Fetch API. Thank you so much!
Updated the answer based on below suggestions in comments.
Many people use this: which is an anti-pattern, What is the explicit promise construction antipattern and how do I avoid it?
Not to Use:
You can wrap your fetch call in a promise and return as a promise.
as an example
function testfunction(arg1, arg2) {
return new Promise((resolve, reject) => {
fetch(apiPath)
.then(resp => {
if (resp.ok) return resp.json()
})
.then(resp => resolve(resp)).catch(err => {
reject(err)
})
})
}
Calling the same from anywhere from your application
testfunction(arg1, arg2)
.then(result=> {
// here you can use the API result
})
.catch(error => {
throw error;
});
This is how you can call your fetch API method and use it at all the places:
function testfunction(arg1, arg2) {
const request = baseApi.InitialiseGetRequest();
return fetch(apiPath).then(resp => {
if (resp.ok) return resp.json()
})
.then(resp => resolve(resp)).catch(err => {
reject(err)
})
}
This is how you can use it accross the application:
testfunction(arg1, arg2)
.then(result=> {
// here you can use the API result
})
.catch(error => {
throw error;
});

Why is function returning undefined when calling fetch, axios, ajax, or a promise [duplicate]

This question already has answers here:
How do I return the response from an asynchronous call?
(41 answers)
Closed 3 years ago.
When attempting to use asynchronous calls, the response is undefined rather than returning back the expected response data.
You can see how to run into this issue with any of the following examples:
Fetch
const response = getDataWithFetch();
console.log(response);
function getDataWithFetch() {
const url = 'https://jsonplaceholder.typicode.com/todos/1';
fetch(url).then(response => {
return response.json();
});
}
Axios
const response = getDataWithAxios();
console.log(response);
function getDataWithAxios() {
const url = 'https://jsonplaceholder.typicode.com/todos/1';
axios.get(url).then(response => {
return response.data;
});
}
<script src="https://unpkg.com/axios/dist/axios.min.js"></script>
AJAX / jQuery $.get
const response = getDataWithAjax();
console.log(response);
function getDataWithAjax() {
const url = 'https://jsonplaceholder.typicode.com/todos/1';
$.get(url, (response) => {
return response;
});
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
Promise
const response = getDataWithPromise();
console.log(response);
function getDataWithPromise() {
new Promise(resolve => {
const response = 'I am a response';
return resolve(response);
});
}
Overview
Asynchronous calls, such as HTTP get requests via fetch, axios, AJAX or other asynchronous calls via Promises all behave similarly in that what is returned by the time that the function reaches its last line is not actually the result that you want.
Explanation of Issue
This is because asynchronous calls do not wait for the call to finish executing before executing the next line:
Note that in all the examples below, I will be using fetch but the concept could easily be extended to the other call types.
getDataWithFetch();
function getDataWithFetch() {
console.log('start of outer function');
const url = 'https://jsonplaceholder.typicode.com/todos/1';
fetch(url).then(response => {
console.log('start of async function');
response.json();
console.log('end of async function');
});
console.log('end of outer function');
}
As you can see via Run code snippet, the outer function starts and ends before the async function starts. What is undefined being returned from getDataWithFetch() because it is hitting the end of the function, and has no return statement. If we change the code to add a return statement, we can see what part of the function call is actually being returned:
const response = getDataWithFetch();
console.log(response);
function getDataWithFetch() {
const url = 'https://jsonplaceholder.typicode.com/todos/1';
fetch(url).then(response => {
return response.json();
});
return 'hello world';
}
Now the function isn't returning undefined anymore, but it's not returning the fetch request response; instead, it is returning 'hello world'. This is because the fetch request is not blocking the rest of the getDataWithFetch function from executing. To understand why, we need to understand what fetch is doing behind the scenes.
Promises
Resolve
To do so, we need to understand Javascript Promises. There's a great beginner-friendly read on it here: https://javascript.info/promise-basics, but in a nutshell:
A Promise is an object that, when it finishes the asynchronous work, it will call the "resolve" function. Where "resolve" returns the expected data via resolve(dataToReturn).
So behind the scenes, fetch returns a Promise that will call resolve when the fetched data is retrieved, like so:
// just an example of what fetch somewhat looks like behind the scenes
function fetch(url) {
return new Promise(resolve => {
// waiting for response to come back from server
// maybe doing a bit of additional work
resolve(responseDataFromServer);
});
}
With that understanding, we now know that we need to return the call to fetch to actually return the Promise, like so:
const response = getDataWithFetch();
console.log(response);
function getDataWithFetch() {
const url = 'https://jsonplaceholder.typicode.com/todos/1';
return fetch(url).then(response => response.json());
}
The console.log is no longer returning undefined, but it's not returning the right response either. That's because getDataWithFetch is now returning the Promise object from fetch. It's time to get the response from resolve.
.then
To get the data passed to the resolve function, we need to use the then method on the returned Promise.
A Promise provides the method then to retrieve the value passed to resolve as such: myReturnedPromise.then(dataToReturn => { /* do something with the data */ });. then will only execute once the dataToReturn is ready to be handled.
Final Solution
So by using the then method, we can get the actual response printed to console:
getDataWithFetch().then(response => {
console.log(response);
});
function getDataWithFetch() {
const url = 'https://jsonplaceholder.typicode.com/todos/1';
return fetch(url).then(response => response.json());
}
And there you have it!
Overly Verbose Solution
If there was too much magic in what made that solution work, you can also see the breakdown of it by explicitly returning a Promise to be fulfilled:
getDataWithFetch().then(response => {
console.log(response);
});
function getDataWithFetch() {
return new Promise(resolve => {
const url = 'https://jsonplaceholder.typicode.com/todos/1';
return fetch(url).then(response => {
resolve(response.json()));
// Note that the `resolve` does not have to be part of the `return`
}
});
}
Async and Await Solution
Recent improvements to Javascript have provided us with a more succinct way of handling Promises to make them appear synchronous instead of asynchronous. Note that the following code is equivalent to the above solutions, but much easier to read:
async function getDataWithFetch() {
const url = 'https://jsonplaceholder.typicode.com/todos/1';
const response = await fetch(url);
return response.json();
}
getDataWithFetch().then(response => {
console.log(response);
});
async and await are clean and powerful alternatives to explicitly dealing with Promises, especially when Promise Chaining is involved. I recommend reading more about them here: https://javascript.info/async-await.

nested, depended callback functions

I am quite new to the callback hell (but i understand its meaning more and more now)
The setup:
getAccessToken: call to get an accesstoken from an api
getUserID:with access token, get a userID from an api
getUserDetails: With userID get userdetails from an api
postUserDetails: post retrieveduserdetails to an api
I need to pass values down the calls:
getAccessToken token -> getUserID(token) userID
->getUserDetails(userID) userDetails -> postUserDetails(userDetails)
in my naivity i thought i could get something running like:
postUserDetails(getUserDetails(getUserID(getAccessToken())));
or the other way round (where i would need to change the naming convention but i tried around so much that i ended up entangled in the below
getAccessToken(getUserID(getUserDetails(postUserDetails)))
What is the logical structure to get something like the below with asynchronous ajax calls working? How can I pass down multiple callbacks that get values from the previous call?
Am i relying on any framework (like async) to get a postUserDetails(getUserDetails(getUserID(getAccessToken()))) working?
I need to pass values down the calls in example getAccessToken -> getUserID ->getUserDetails -> postUserDetails I don't know if that was clear from my question
yes, values, but not the promises themselves.
a simple mock of your code:
//your call chain
getAccessToken()
.then(getUserID)
.then(getUserDetails)
.then(postUserDetails)
.then(() => {
console.log("Done");
})
//a mock of your code
//a utility to "simulate" the delay of a server call and return a Promise
function wait(delay) {
return new Promise(resolve => setTimeout(resolve, delay));
}
function getAccessToken() {
console.log("getAccessToken");
//mock the request to the server
return wait(Math.random() * 1000+500)
.then(() => {
console.log("generating a token");
return Math.random().toString(36).slice(2)
});
}
function getUserID(token) {
console.log("getUserID(%o)", token);
//mock the request to the server
return wait(Math.random() * 1000+500)
.then(() => {
console.log("returning userId");
return "id-1234";
});
}
function getUserDetails(userId) {
console.log("getUserDetails(%o)", userId);
//mock the request to the server
return wait(Math.random() * 1000+500)
.then(() => {
console.log("returning user");
return {
id: userId,
firstName: "Zaphod",
lastName: "Beeblebrox"
}
});
}
function postUserDetails(user) {
console.log("postUserDetails(%o)", user);
return wait(Math.random() * 1000+500)
.then(() => {
console.log("userDetails posted");
});
}
.as-console-wrapper{top:0;max-height:100%!important}

Categories