So, I'm trying to bring my function work with async timers logic, where I need to execute computeResult (func for example) after the timer is stop. To get the setTimeout async logic under control I had used the Promise based asyncFunc function, but it always return me Promise {<pending>} when I used it.
Where is my fall in this case? Thank you.
P.S.
I also see the various posts on this topic on SoF, but it does not help me. Do not block my question just to grow up your EXP on SoF
const computeResult = () => {
return 'sdas'
}
const asyncFunc = () => new Promise(
r => setTimeout(r, 1000))
.then(() => computeResult()
);
export default asyncFunc
Not 100% sure what your trying to do,.
But the following might be what your after.
const computeResult = () => {
return 'sdas'
}
const asyncFunc = () => new Promise(resolve =>
setTimeout(() => resolve(computeResult()), 1000)
);
console.log("Wait for computeResult");
asyncFunc().then(r => console.log(r));
You write all right in this case, except one little think. You forgot to execute the Promise after it has been resolved, that's why it stuck on <pending> state.
So, in another words just write after the asyncFunc invoke the .then tail in like next way asyncFunc().then(your_sersult => ddoSomething(your_sersult))
That's all. You will get what you want :)
You can read more about it on the MDN site:
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise
Related
This question already has answers here:
Why does nodejs stop execution of while true loop inside async function
(3 answers)
Closed 28 days ago.
I have a sample code on TS playground represents my problem.
In an async function, I log the result after await for the promise, but only the code inside promise run, not the log outside of it. Could someone explain this problem?
Here is the code:
const asyncFnc = async () => {
let result = false;
await new Promise(resolve => {
setTimeout(() => {
// This log worked
console.log('waited 5s');
result = true;
}, 5000);
});
// This log did not worked
console.log(result);
}
asyncFnc();
And the result:
await sends the parent function to sleep until the promise on the right hand side settles (i.e. resolves or rejects).
Your promise never resolves or rejects. (i.e. you don't call resolve, make use of the second argument, or throw an exception).
Thus the parent function sleeps forever.
The idiomatic way to write this would be to avoid setting variables in the wider scope as a side effect, and just resolve with the values instead.
const asyncFnc = async () => {
const result = await new Promise(resolve => {
setTimeout(() => {
console.log('waited 5s');
resolve(true);
}, 5000);
});
console.log(result);
}
asyncFnc();
You need to call resolve() in your timeout
lets say this is what we have:
onClick={() => restrictOrders()}>
and this is our async function
const restrictOrders = async () => {
try {
const result = await axios.post(`${config.dev_server}/something`);
}catch(e){}
}
do I need to change the onClick to
onClick={async() => await restrictOrders()}>
the results is the same, I've tested it in both production and local, with high and low internet speed, added long timeouts on the server and in all cases it seems to be waiting for the response.
Short answer - no, it makes no difference.
In general, an await basically means "wait for this Promise to resolve before continuing with the rest of the code". But this means an await before the final statement of a function has no effect whatsoever, because there is nothing that needs to happen after the "wait".
As a simple illustration:
const sleep = (delay) => new Promise(resolve => setTimeout(resolve, delay));
const myFunction = async () => {
console.log("before");
await sleep(2000);
console.log("after");
};
myFunction();
and, as you would no doubt expect, there is a 2 second delay between the "before" and "after".
But if you didn't care about the "after", then you don't need the await at all:
const sleep = (delay) => new Promise(resolve => setTimeout(resolve, delay));
const myFunction = async () => {
console.log("before");
sleep(2000);
};
myFunction();
You could use await here, and maybe it's better practice in case you later want to add something after - but there's absolutely no need for it. (In this trivial case of course there's no need to have the sleep call at all - but imagine it's a "real" function with a side-effect, like posting to an API.)
It's the same with your React example:
onClick={async() => await restrictOrders()}>
you are awaiting the last (and in this case, only) statement of an anonymous function. Since there's nothing you do afterwards that needs to wait, there's no harm in not having the await there - so most commonly it's not done, I guess to make the inline function expression less verbose.
What has been observed (by me) on some of the apps is this:
Actions taken by the user (such as button-click) result in updating the state. So, in this case, if we have a state-variable like this:
const [ordersRestricted, setOrdersRestricted] = useState(0);
then, the click-handler is like so:
onClick={() => setOrdersRestricted(prev => (prev + 1))}>
Consequently, the corresponding effect (or side-effect) from the action is handled like so:
useEffect(() => {
const restrictOrders = async () => {
try {
const result = await axios.post(`${config.dev_server}/something`);
// do something with the result
// typically, update the state (but take care to not change
// dependencies, that may lead to infinite looping)
} catch(e){}
};
restrictOrders();
}, [ordersRestricted]);
I have this script that get random videos in the web.
The function getRandomVideo() returns a Promise with a list of urls.
What I want to do is print the data by calling main().then(data => console.log(data)). The problem is that data is being printed before the function is done running. So when I start the program I get undefined and then after the function is done I get the actual data.
I thought that what is inside .then() would run just after the promise is returned.
Does anyone know what is happening?
const main = async () => {
let allData = [];
getRandomVideo().then((videoLinks) => {
allData = videoLinks;
return new Promise((resolve) => {
resolve(allData);
});
});
};
main().then((data) => console.log(data));
As others have said, your code is full of anti-patterns in an attempt to avoid the basics of asynchronous programming with promises. All you need is this:
const main = function() => {
return getRandomVideo();
}
main().then(data => console.log(data)).catch(err => console.log(err));
Or, of course, you don't even need main() at all. You can just do:
getRandomVideo().then(data => console.log(data)).catch(err => console.log(err));
Some anti-patterns in your original code:
Not returning anything from your main() function.
Assigning an asynchronous value to a higher scoped variable and hoping that it somehow fixes asynchronous programming issues. It is nearly always a warning that you're doing something wrong when you assign an asynchronously retrieved value to a higher scoped variable because the code in the higher scope generally has no idea when that value will actually be available. Only within the .then() handler so you know when the value is present.
Manually creating a promise for no particular reason.
Marking a function async for no reason.
No error handling (e.g. .catch() handler) when calling a function that returns a promise.
If you want to manipulate the results of getRandomVideo before you send it back to the caller, then you could have a reason to use async/await
const main = async () => {
let videos = await getRandomVideo();
// define some filter function for the videos array and
// return the filtered result as the resolved value
return videos.filter(...);
}
main().then(data => console.log(data)).catch(err => console.log(err));
I think we can understand main function step by step:
let allData = []; //Defined allData variable
call getRandomVideo function
The thread wait getRandomVideo completed and return videoLinks array when you call .then function
allData = videoLinks; //Asign videoLinks array to allData variable
return new Promise((resolve) => { resolve(allData) }) //Return a Promise, that will tell the thread that you will do that later, on the other hand, after few second it's will comback and return all data by call resolve function
End of main function
==>> So as you can see, main function don't return a Promise imediately, you can't use .then to wait until it completed
Solution: Let wrap main function detail by a Promise like that
const main= () => {
return new Promise((resolve) => {
getRandomVideo().then((videoLinks) => {
resolve(videoLinks);
});
});
}
And clean your code by drop allData variable.
Is there any typescript config option or some workaround to check if there is no resolve called in new Promise callback?
Assume I have a promise
new Promise(async (resolve, reject) => {
try {
// do some stuff
// but not calling resolve()
} catch (e) {
reject(e)
}
})
I want typescript to warn me that I did not call resolve(). Is it possible?
I know that I can use noUnusedParameters, but there are a couple of situations where I still need unused parameters (e.g. request inside of express.Hanlder, where I only use response, etc.)
No, that is not possible. Knowing whether code calls a certain function (resolve in this case) is just as hard as the halting problem. There is proof that no algorithm exists that can always determine this.
To illustrate, let's assume that the algorithm for determining whether a function calls resolve exists, and is made available via the function callsResolve(func). So callsResolve(func) will return true when it determines that func will call resolve (without actually running func), and false when it determines that func will not call resolve.
Now image this func:
function func() {
if (!callsResolve(func)) resolve();
}
... now we have a paradox: whatever this call of callsResolve returned, it was wrong. So for instance, if the implementation of callsResolve would have simulated an execution of func (synchronously) and determines that after a predefined timeout it should return false, the above is a demonstration of a function that calls resolve just after that timeout expired.
The closest you can get to a compile time check is to use async / await syntax.
If you don't want to use that, you could timeout your promises, though you would have to do that with each of your promise after / when you are creating them.
A solution could look like this:
export const resolveAfterDelay = (timeout: number) => new Promise((r) => setTimeout(r, timeout));
export const rejectAfterDelay = async (timeout: number) => {
return new Promise((resolve, reject) => setTimeout(() => reject(`Promise timed out as resolve was not called within ${timeout}ms`), timeout));
};
export const timeoutPromise = <T>(timeout: number) => async (p: Promise<T>): Promise<T> => {
return Promise.race([p, rejectAfterDelay(timeout)]);
};
const timeoutAfter1s = timeoutPromise(1e3);
const timeoutAfter10s = timeoutPromise(10e3);
timeoutAfter10s(resolveAfterDelay(3e3)).then(success => console.log("SUCCESS IS PRINTED")).catch(console.error); // works
timeoutAfter1s(resolveAfterDelay(3e3)).then(success => console.log("NEVER REACHED")).catch(console.error); // aborts
const neverResolvingPromise = new Promise(() => {
});
timeoutAfter1s(neverResolvingPromise).catch(console.error); // promise never resolves but it will be rejected by the timeout
It makes use of Promise.race. Basically, whatever first resoves or rejects will be returned. We want to always reject a Promise if it does not resolve in time.
You would always have to wrap your Promise on creation like
timeoutAfter10s(new Promise(...));
And you would have to adapt the timeout according to your use case.
I was wondering what is the proper way to call a promise after another promise resolves. I know we can use async await to create functions which will resolve a promise. I was wondering which form of handling promises is consider proper practice, or is it good practice to create generators instead? consider the following code:
const fetchSomething = () => new Promise((resolve) => {
setTimeout(() => resolve(console.log('future value')), 500);
});
const fetchSomethingElse = () => new Promise((resolve) => {
setTimeout(() => resolve(console.log('future value dueeee')), 3000);
});
const get = () => {
return fetchSomething().then(function(){
fetchSomethingElse()
});
}
get();
or
const fetchSomething = () => new Promise((resolve) => {
setTimeout(() => resolve({resolve: true}), 500);
});
const fetchSomethingElse = () => new Promise((resolve) => {
setTimeout(() => resolve({resolve: true}), 3000);
});
const get = async function() {
const fet = await fetchSomething();
const fet2 = await fetchSomethingElse();
};
get();
Either one is fine. Your choice.
In the first you're nesting .then() handlers. In the second, you're using the newer await to sequence them. More people are moving to await because it appears to be simpler code for sequencing operations (assuming you do proper error handling), though in this case, they are pretty similar in complexity, particularly with the simplification suggested below so it's really up to your own personal coding style.
What is missing in both is that get() just returned a promise so you need to use .then() and .catch() with it to get the value and catch any errors.
Also, something that is missing in the first is that you aren't returning the second promise which means the caller won't know when the second operation is done.
Your first can be simplified and fixed up like this:
const get = () => {
return fetchSomething().then(fetchSomethingElse);
}
get().then(val => {
// done here
}).catch(err => {
// error here
});
As Pointy mentioned, you don't "call a promise". You "call a function that returns a promise". Promises are objects. They are not callable.
Probably what your title could be rewritten to is: "Correct way to sequence two asynchronous operations that each return a promise".
For completeness, if your two async operations don't depend upon one another, then you don't have to manually sequence them. You can start them both and then monitor when both are done. This will sometimes get a faster end-to-end response.
You can do that using Promise.all():
const get = function() {
return Promise.all([fetchSomething(), fetchSomethingElse()]).then(results => {
// process results array and then return final value
// results[0] is result from fetchSomething, results[1] is result from fetchSomethingElse
return finalVal;
});
}
Both are fine, but you are making a common mistake in the top example (and maybe it's just because of the simplification of the code for the question). You are returning the promise from get, but you are not returning the promise from the then. This means the caller of get won't know when both promises have resolved. Consider:
const fetchSomething = () => new Promise((resolve) => {
setTimeout(() => resolve(console.log('future value')), 500);
});
const fetchSomethingElse = () => new Promise((resolve) => {
setTimeout(() => resolve(console.log('future value dueeee')), 3000);
});
const get = () => {
return fetchSomething().then(function(){
fetchSomethingElse()
});
}
// we don't when fetchSomethingElse is done
get().then(() => console.log("done"));
Also there's another option you might consider since the second promise doesn't depend on the output of the first. Call them in parallel:
const get = () => {
return Promise.all([fetchSomething(), fetchSomethingElse() ])
}
In this case one can start before the other is finished and the whole operation should be faster.
It's important to remember that in Promise-based patterns you're using functions that return Promises. Promises are passed in resolve and reject arguments (which are themselves functions). What you resolve with, is what gets exectuted in the .then() phase, and what you reject with gets exectuted in the .catch() phase.
To handle Promises in sequence, you're passing your values into the top-level function that wraps the Promise.
so...
const p1 = () => {
return new Promise((resolve,reject) => {
window.setTimeout(() => {
resolve('future value one');
},500);
});
};
const p2 = (v1) => {
return new Promise((resolve,reject) => {
window.setTimeout(() => {
const v2 = 'future value two';
resolve({v1,v2});
},500);
});
};
p1().then(p2).then(console.log);