Resolve an Await function if it takes more than x seconds in Javascript / Node.js - javascript

I have an await function() which waits for an external API. Usually, these APIs take 600ms to return data. But sometimes it's taking around 10 seconds. So how do I write a function so that if it takes more than 1 second, then break that function and resolve anyway (don’t wait for the API)
Here's the main function code which calls the API.
console.log("Before Await");
await fetchExternalAPI("data");
console.log("Continue if success or takes more than x seconds");
The actual function:
const fetch = require('node-fetch');
module.exports = async function fetchExternalAPI(data) {
try {
const response = await fetch("https://www.example.com/api/log", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify(data)
});
const returnData = await response.json();
console.log("Response: " + JSON.stringify(returnData))
return returnData
}
catch (e) {
console.log("Error: " + e)
return e
}
}
I want to return success fo the above function
if fetch is success
or if exceeded more than x seconds
Both should return success no matter the real output.

You can use AbortController to abort the fetch method, inside a setTimeout with 1sec, and if the fetch resolved before the setTimeout callback execute, then clearTimeout
const fetch = require('node-fetch');
module.exports = async function fetchExternalAPI(data) {
try {
let controller = new AbortController();
let timeId = setTimeout(() => {
controller.abort()
}, 1000)
const response = await fetch("https://www.example.com/api/log", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify(data)
signal: controller.signal
});
clearTimeout(timeId)
const returnData = await response.json();
console.log("Response: " + JSON.stringify(returnData))
return returnData
}
catch (e) {
console.log("Error: " + e)
return e
}
}

I think you need to use Axios.
npm i axios
than in body you have one property timeout.
const instance = await axios.create({
baseURL: 'https://some-domain.com/api/',
timeout: 1000,
headers: {'X-Custom-Header': 'foobar'}
});

You can add a promise in your call to resolve on time and reject if too long.
The following code has not been tested but that's the idea
const fetch = require('node-fetch');
module.exports = async function fetchExternalAPI(data) {
try {
const response = await fetch("https://www.example.com/api/log", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify(data)
});
await new Promise((resolve, reject) => {
setTimeout(reject, 50000));
resolve();
}).then(() => {
const returnData = await response.json();
console.log("Response: " + JSON.stringify(returnData))
return returnData
}).catch(() => {
return 'Took too long';
}));
}
catch (e) {
console.log("Error: " + e)
return e
}
}

Related

Fetch request not returning any data in console, or on front end

When I submit my form, I am not returning any data, not even in my console. I am trying to return details from WHOIS regarding the URL that is searched, and am getting nothing back.
Can anyone provide any advice as to why this might be the case?
Here is my front end script tag, after my form:
document.getElementById('search').addEventListener('submit', function(e) { e.preventDefault(); getDetails(); })
async function getDetails(url = `http://localhost:3000/lookup/${url}`, data = {}) {
return new Promise(function (resolve, reject) {
fetch(url, {
method: 'GET',
headers: {
'Content-Type': 'text/html'
},
body: JSON.stringify(data)
}).then(async response => {
if (response.ok) {
response.json().then(json => resolve(json))
console.log(data);
} else {
response.json().then(json => reject(json))
}
}).catch(async error => {
reject(error)
})
})
}
On my express backend I am using req.params.url if that helps provide any context at all...
My Status Code is 200, and all appears to be normal in the Headers tab...
You have a mix of promise and async syntax, which is confusing, let's translate it first by unpicking the promise and then into await (if you can use async then do, it's easer than Promise/then):
document.getElementById('search').addEventListener(
'submit',
function(e) {
e.preventDefault();
getDetails();
});
async function getDetails(url = `http://localhost:3000/lookup/${url}`, data = {})
{
// Fetch will throw an exception if it can't connect to the service
const response = await fetch(url, {
method: 'GET',
headers: { 'Content-Type': 'text/html' },
body: JSON.stringify(data)
});
if (response.ok)
return await response.json();
// We could connect but got an error back from the service
// There may not be a response body, so response.json() or .body() might crash
throw new Error(`Error from server ${response.status}: ${response.statusText}`;
// We don't need catch, as any exception in an await will cascade up anyway
}
This makes it much more readable, and it's apparent that getDetails doesn't make any changes itself, it just returns the JSON from the service. The fix needs to be in the event listener - it needs to do something with that result:
document.getElementById('search').addEventListener(
'submit',
async e => {
e.preventDefault();
const searchResult = await getDetails();
// Do something to show the results, populate #results
const resultsElement = document.getElementById('results');
resultsElement.innerText = JSON.stringify(searchResult);
});
You are mis-using async/await. Try this:
async function getDetails(url = `http://localhost:3000/lookup/${url}`, data = {}) {
const response = await fetch(url, {
method: 'GET',
headers: {
'Content-Type': 'text/html'
},
body: JSON.stringify(data)
});
const json = await response.json();
if (response.ok) {
console.log(json);
return json;
} else {
throw new Error(json);
}
}
In essence await is a replacement for then (but you can only use it in functions marked with async).

Javascript in VueJs: how to return data object from async fetch instead of Promise

I have this action in store
actions: {
testLogin(context, credentials) {
const loginService = new FetchClient();
let d = loginService.post('login', credentials);
console.log(d);
},
and this function in another class imported to store
async post(endpoint, params) {
await fetch(this.url + endpoint, {
'method': 'POST',
headers: this.headers,
body: JSON.stringify(params),
})
.then(response => {
return response.json();
})
.then( (data) => {
this.returnData = data.data;
})
.catch(error => {
console.log(error);
});
return this.returnData;
}
And I get Promise {<pending>} which I can extract data from inside the fetch class but can't access data if I'm in the store because it's a Promise not an object. How can I solve this?
Put the return statement inside the second then block:
async post(endpoint, params) {
await fetch(this.url + endpoint, {
'method': 'POST',
headers: this.headers,
body: JSON.stringify(params),
})
.then(response => {
return response.json();
})
.then( (data) => {
this.returnData = data.data;
return this.returnData;
})
.catch(error => {
console.log(error);
});
}
I would even recommend you use the following code for better legibility:
async post(endpoint, params) {
const response = await fetch(this.url + endpoint, {
'method': 'POST',
headers: this.headers,
body: JSON.stringify(params),
})
if (!response.ok) {
const message = `An error has occured: ${response.status}`;
throw new Error(message);
}
const resp_data = await response.json()
return resp_data.data
}
Then call your method like so:
post(endpoint, params)
.then(data => {// do something with data})
.catch(error => {
error.message; // 'An error has occurred: 404'
});
refer to this async/await guide
Can you try:
async testLogin(context, credentials) {
const loginService = new FetchClient();
let d = await loginService.post('login', credentials);
console.log(d);
}
As #Ayudh mentioned, try the following code:
async post(endpoint, params) {
try{
let response = await fetch(this.url + endpoint, {
'method': 'POST',
headers: this.headers,
body: JSON.stringify(params),
});
let data = await response.json();
this.returnData = data.data;
}catch(e){
console.log(e);
}
return this.returnData;
}

JavaScript : Testing Promises with Timeout and conditions

I am trying to test my REST APIs using Mocha and Chai.
Below is code snippet
"use strict"
// all required varibles modules declaration
describe("Get Data", function () {
it("Get Final Data", function () {
return getFinalResponse(
"https://localhost/getdata",
"GET",
JSON.stringify(rawdata)
)
.then((response) => response)
.then(async (response) => {
console.log(response)
let jsonData = await response.json()
operationId = jsonData.operation
console.log(jsonData.operation)
})
})
})
function delay(t) {
return new Promise((resolve) => setTimeout(resolve, t))
}
async function restCall(url, method, data) {
const signer = new common.DefaultRequestSigner(provider)
const httpRequest = {
uri: url,
headers: new Headers(),
method: method,
body: data,
}
await signer.signHttpRequest(httpRequest)
const response = await fetch(
new Request(httpRequest.uri, {
method: httpRequest.method,
headers: httpRequest.headers,
body: httpRequest.body,
})
)
return response
}
async function getFinalResponse(url, method, data) {
let response
do {
await delay(10000)
response = await restCall(url + "/done", method, data)
} while ((await response.json()["status"]) !== "DONE")
return restCall(url, method)
}
The condition while(await response.json()["status"] !== 'DONE') breaks/terminates in 1 minutes but even after putting 2 minutes as mocha time out . I am not getting the expected output.
Kindly help me what might be the issue.

Vue - returning the result of a synchronous method

I'm struggling to return the synchronous results of the method below.
I call the method from a different method:
var result = this.getVendor(id)
console.log(result)
Here is the fetch method:
methods: {
async getData(id) {
const response = await fetch(`${API_URL}api/${id}`, {
method: "GET",
headers: {
authorization: `Bearer ${localStorage.token}`
}
})
.then(res => res.json())
.then(data => {
return data;
});
await response;
}
}
How do I return the results response of the getData() function to show in the console?
Async functions Always return a promise.
You can use the await syntax to return it properly.
async getData(id) {
const response = await fetch(`${API_URL}api/${id}`, {
method: "GET",
headers: {
authorization: `Bearer ${localStorage.token}`
}
})
const data = await response.json()
return data
}
You can access the data out of that function anywhere you call it.
let data = null
object.getData(2)
.then(x => {
data = x
})
Also if you are going to use async await make sure to use try and catch to handle any errors that come up.
async getData(id) {
try {
const response = await fetch(`${API_URL}api/${id}`, {
method: "GET",
headers: {
authorization: `Bearer ${localStorage.token}`
}
})
const data = await response.json()
return data
}
} catch(err) {
console.error(err)
}

Return resolved promise value

const displayCharacters = async () => {
if(filteredCharacters !== 'default'){
const a = filteredCharacters.map(e => e.name);
const options = {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ 'data' : a })
};
const b = await fetch("/image",options).then(res => res.json())
return b;
}else{
return "yikes";
}
}
console.log(displayCharacters());
I have this fetch request but when I log the results this is what i see :
Promise {<resolved>: "yikes"}
__proto__: Promise
[[PromiseStatus]]: "resolved"
[[PromiseValue]]: "yikes"
I just want the promiseValue and not this whole thing around it. How do i do this?
the async function returns a promise instantly, without waiting for the promise to resolve. You may instead console.log inside the function:
const displayCharacters = async () => {
if(filteredCharacters !== 'default'){
const a = filteredCharacters.map(e => e.name);
const options = {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ 'data' : a })
};
try {
const b = await fetch("/image",options).then(res => res.json());
console.log(b);
//the better practice is however, to do like:
const b = await fetch("/image",options)
const result = await b.json();
console.log(result );
}
catch(err) {
console.log(err);
}
}else{
console.log("yikes");
}
}
displayCharacters();
The best way that i know to use fetch goes something like this:
const displayCharacters = async () => {
if(filteredCharacters !== 'default'){
const a = filteredCharacters.map(e => e.name);
const options = {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ 'data' : a })
};
const b = await fetch("/image",options)
.then(res => {
// Handle API Errors
if (!res.ok) {
throw Error(res.statusText);
}
// Return if no errors
return res.json();
})
// this is the data you want
.then(data => data)
// it will only reject on network failure or if anything prevented the request from completing
.catch(error => {
console.log(error.message)
});
return b;
}else{
return "yikes";
}
}
Basically you chain two thens and a catch to completely understand the response
- the first then checks for api level errors
- second then gets you the data
- catch is invoked in case when it is not able to reach the api itself like connection issues

Categories

Resources