How to await data from async/await debounced axios call - javascript

I'm trying to return a debounced search result from an API request using lodash's debounce function but keep getting undefined from the call.
Here's my code, please help;
const searchSuggestionsRequest = async (input) => {
const params = {
userInput: encodeURIComponent(input),
};
const { data } = await axios.get(`${BASE_URL}/api/location`, { params });
return data;
};
const debouncedSuggestionsRequest = _.debounce(searchSuggestionsRequest, 500);
const fetchSearchSuggestions = (input) => {
return debouncedSuggestionsRequest(input);
};
handleSearchSuggestions = async (input) => {
const searchObj = await fetchSearchSuggestions(input);
console.log('searchObj', searchObj);
};
handleSearchSuggestions()

You are expecting the debounce function to return the result of your original function, or in your case the resolved promise. But that is not how the debounce function works.
The debounce function wraps your function with its own code in which it checks if any new call files in our not. After a certain amount of time eventually your function is initiated. But it cannot return the result of that function.
You need to define a more global scope (or at least a scope overlapping your functions) variable, and set that variable in your function where you get the axios result.
You problem remains that you cannot await for the result, so your console.log will still be undefined. Personally I develop in Vue, and I can set a reactivity watcher on the variable.

Related

Calling a callback function that returns a setTimeOut callback javascript

I tried searching for this, but was unable to find something that matched the case I needed.
I have this function here that can't be modified:
function generate() {
const delay = 7000 + Math.random() * 7000;
const num = Math.random();
return (callback) => {
setTimeout(() => {
callback(num);
}, delay);
};
}
When I try to call the function like generate() I get an error. I've also tried using a promise based approach like:
const result = await generate();
return result;
But when I do that the result that is returned is a promise, which I can't render into JSX (I'm using React).
JSX component code (For debugging purposes currently):
const Test = () => {
return <>{generate()}</>;
};
I would appreciate any suggestions here. Thanks!
This in no way is to solve your problem, but only to understand this logic. Maybe it can help in solving the problem.
generate() in this case returns a function that sets the timeout.
typeof generate() = "function"
// this is then the function we use to run the long running function
const generateFunction = generate();
//we then create the callback handler function
const callback = () => {...}
//when executed it will set the timeout to the random delay and display the result in your component of oyur choice.
generateFunction(callback);
fiddle

Using a props Variable in an async Function

I am receiving the value of web3 within a vue.js component. I am successfully using this variable earlier in my code in the same component. However, upon trying to use it in an async function, I receive an undefined error on the said variable. Shouldn't I be able to use it as below, or am I missing something? (blockNum is being assigned a value earlier in my code.)
props: ['web3']
async function getTimestamp () {
const block = await this.web3.eth.getBlock(blockNum)
const ts = await this.web3.eth.getBlock(block).timestamp
return ts
}
console.log(getTimestamp())
Since getTimestamp is an async function, it returns a promise as opposed to the actual value. Therefore, you have to wait for the promise to resolve before console logging it.
async function getTimestamp () {
const block = await this.web3.eth.getBlock(blockNum)
const ts = await this.web3.eth.getBlock(block).timestamp
return ts
}
getTimestamp().then(ts => {
console.log(ts)
}

Why does the function getSafestCountriesNames() return promise{pending} when i call it and when i use async/await it returns undefined?

This is the function:
export const getSafestCountriesNames = async() => {
try{
const names = await getCountriesData();
names.forEach((country) => {
const {score} = country.advisory;
if(score == 0){
const liEl = createDOMElement('li', {className: 'name'});
liEl.innerHTML = `${country.name}`;
return liEl;
}
});
}
// …
}
When I am calling it in here I get undefined:
async function func(){
const names = await getSafestCountriesNames();
console.log(names);
}
func();
It returns Promise{pending} because when a function has the async keyword, it has to return a Promise. See here.
Nonetheless there's a deeper issue in your code. Your return statement is inside the callback of the forEach. That's the return statement of the callback, not the one of getSafestCountriesNames.
Since you need to return a Promise (for you're using await), you can either wrap your logic inside something like:
return new Promise((resolve, reject) => {
// your logic here, resolving or rejecting instead of just returning
})
or keeping things simple: return Promise.resolve("something")
When you call the function with await, it resolves undefined because you're not resolving anything in your Promise.

How to wait for an async function in javascript at top level?

I know this is a terrible idea. But I have an API which I can't use until I have a piece of data which I can only get asynchronously. Something like this:
const key = await get_async_data(config) // NOT RIGHT, can't use await at top level
const api = new API(key)
... use api ...
This is at top level, outside of any function, so I can't just await get_async_data() (it does return a Promise).
Is there anything short of putting all my code in a giant async function so I can call await?
API is just a class exported by a module (which I control).
(BTW I thought of putting the code to get the key into the API class's constructor, but of course constructors can't be async either.)
I could make every async method of API set the key if unset, but that's pretty invasive and error-prone.
So I'm not really asking how to make my top-level code wait as much as I'm looking for alternative ways to structure this so the async call happens cleanly.
Here's some more detail in case this helps.
In api.js:
class API {
constructor(key) {
this.key = key
// other stuff
}
async f1(a) {
}
async f2(b, c) {
}
f3() {
return true
}
}
export default API
Then in the places (many) where it'll be used:
import API from '#/api'
const key = async get_key() // NOPE
const theAPI = new API(key)
async importantMethod(args)
{
return await theAPI.f1(args)
}
async otherMethod()
{
if (theAPI.f3)
return await theAPI.f2(123)
// etc...
}
// ... and so on
Just use the Promise:
const pendingAPI = get_async_data(config).then(key => new API(key)); // N.B. no await
export default pendingAPI;
Meanwhile, in another file...
import pendingAPI from 'other/file';
pendingAPI.then(doStuffWithAPI);
There are times when async/await is a win. But never forget it's just sugar over Promises.
top level is a terrible idea, yes. But I don't see why you can't just put it in a function?
const getAsync = async () => {
const key = await get_async_data(config);
return key;
}
getAsync().then(key => {
const api = new API(key)
}
If you want to alter your existing code as little as possible, I'd consider changing the entry point to a module which gets the key, and then calls the module which instantiates the API (the old entry point). For example:
// start.js
import makeApi from './makeApi';
get_key()
.then(makeApi);
// makeApi.js
export default function(key) {
const theApi = new API(key);
function importantMethod(args) {
return theAPI.f1(args)
}
function otherMethod() {
if (theAPI.f3)
return theAPI.f2(123)
}
// etc
}
In short, all you have to do is wrap your current entry point in a function.

Why does Typescript think async/await returns a value wrapped in a promise?

I want to refactor a promise chain into async/await, but Typescript is complaining about the typing.
TS2322:Type 'IHttpPromiseCallbackArg< IResp >' is not assignable to type 'IResp'...
I thought await would return a regular value, not a promise. Am I wrong? If so, how can I assign a typing so that the desired code will compile?
I thought await would return the same value as the first argument in the .then callback. Am I wrong?
Old code:
handleSubmit(form) {
const params = this.getParams(form);
this.myAsyncRequest(params)
.then((resp:IResp) => this.processResp(resp))
.catch((e:IServerError) => this.handleError(e));
}
Desired new code:
async handleSubmit(form) {
const params = this.getParams(form);
try {
const resp:IResp = await this.myAsyncRequest(params); //typing error with "const resp:IResp"
this.processResp(resp);
} catch (e:IServerError) {
this.handleError(e);
}
}
The desired code still breaks if I remove the return type in myAsyncRequest ; I guess Typescript infers directly from the AngularJS library.
myAsyncRequest(params:IParams):IHttpPromise<IResp> {
return $http.post('blah', data);
}
If I remove "IResp" from the const resp declaration, processResponse complains that IHttp< IResp> does not equal IResp...
processResp(resp:IResp) {
//do stuff
}
Your question "I thought await would return the same value as the first argument in the .then callback. Am I wrong?".
No, you are absolutely right. But you are wrong about what the first argument in the .then callback is.
You define myAsyncRequest to return IHttpPromise<IResp>. But IHttpPromise<T> is defined as inheriting IPromise the following way:
type IHttpPromise<T> = IPromise<IHttpPromiseCallbackArg<T>>;
So, an IHttpPromise<T> is a promise that returns an IHttpPromiseCallbackArg<T> back where the actual data of type T is in the data property of the IHttpPromiseCallbackArg<T>.
So, the old code variant we see in the question:
handleSubmit(form) {
const params = this.getParams(form);
this.myAsyncRequest(params)
.then((resp:IResp) => this.processResp(resp))
.catch((e:IServerError) => this.handleError(e));
}
should actually not compile without errors in TypeScript when myAsyncRequest is defined to return IHttpPromise.
Howto fix this:
async handleSubmit(form) {
const params = this.getParams(form);
try {
const httpResp:IHttpPromiseCallbackArg<IResp> = await this.myAsyncRequest(params);
const resp: IResp = httpResp.data;
this.processResp(resp);
} catch (e:IServerError) {
this.handleError(e);
}
}
Note: In the latest type definitions for angular, the type IHttpPromiseCallbackArg<T> is actually called IHttpResponse<T>.
Maybe in your code, you have defined IResp as IHttpResponse<Something>? Then you just have a conflict with the old name IHttpPromiseCallbackArg. Then get the newest type definitions from DefinitelyTyped that uses the new name. And you would also have to change the definition of myAsyncRequest to:
myAsyncRequest(params:IParams):IHttpPromise<Something> {
The line containing the await does indeed await the resolved value - but because the function itself is async (to allow all that awaiting), you get back a promise.
Example... in the below code you can use x as a plain number (even though delay returns a promise) and again for y - so all the stuff you await is resolved so you can use it more like you would if it were synchronous.
The async function that doesn't look like it returns a promise, now does.
This can seem confusing, because it seems to "invert the promises", but what it does is shift the then to the top level (you could have async functions calling down to other async functions and so on).
function delay(ms: number) {
return new Promise<number>(function(resolve) {
setTimeout(() => {
resolve(5);
}, ms);
});
}
async function asyncAwait() {
let x = await delay(1000);
console.log(x);
let y = await delay(1000);
console.log(y);
return 'Done';
}
asyncAwait().then((result) => console.log(result));

Categories