I do not really understand the memory model in promises. I'm using Bluebird 2.x.
I have something like this which attempt to connect to a bluetooth device. I want it so that if it fails to connect within a 10 second period, I want to close it and return it as failure.
Control.prototype.connectToAddress = function connectToAddress(address)
{
var self = this;
return new Promise(function(resolve, reject) {
var timer = setTimeout(function() {
reject(new Error('Timeout'));
}, 10000);
self.bt.connectToAddressAndService(address, self.service)
.then(function(dev) {
clearTimeout(timer)
resolve(dev);
})
.then(function error(err) { reject(err); });
});
}
This isn't the actual situation. The real situation is that connectToAddress is resolve immediately without waiting for device to connect. An emitter is set up later to notify the caller that a device is found. But for the sake of the question, suppose connectToAddress actually waits and return the connected device.
When we fail to connect to a device within 10 seconds, perhaps it moves away after we scanned it, the promise is rejected. But, self.bt.connectToAddressAndService call has not been resolved to either resolve or reject. If connectToAddress is repeatedly called in situation like this, does that cause some sort of memory stack to build up and never released? If so, what can I do to avoid this?
Promises are just Javascript objects that are garbage collected just like any other Javascript object. So, as long as you don't keep references to promises indefinitely, they will be garbage collected just like any other object when they are no longer being used.
So, in your specific example, the state you shown in your function will stay alive as long as your timer is still going or .connectToAddressAndService() is still doing something and might still be able to reject or resolve (meaning it still has live references to the resolve or reject handlers). The timer obviously has a fixed life of 10 seconds. But, you don't show the code that is behind .connectToAddressAndService() so we can't really offer any info on how long that might stay alive. It really ought to be the case that it will either resolve or reject at some point so there's no chance it could hang out forever keeping this internal state alive. We'd have to see the code inside of that to know if that is currently a problem or not.
If you've timed out your operation, but .connectToAddressAndService() is still alive and will call it's own resolve or reject handler some long time in the future, then there is memory still in use while it is waiting to eventually finish. Even though the promise has already been rejected, Javascript doesn't know what your other code around .connectToAddressAndService() might be doing so the internal state in the code you show is kept alive and in memory.
Related
If I enter the following into the console, no error is reported to the console
let p = new Promise(function(resolve, reject) {
console.log('started')
reject('immediately reject')
})
console.log('do some other work')
p.catch(function(error) {
console.log('error caught')
})
// Outputs:
// do some other work
// error caught
But if I remove the call to catch an uncaught error is shown. I can even type in the first half, hit enter, see the error, then add the catch and the error goes away. This seems weird to me: how can JavaScript know that a promise will eventually be caught? How would this affect control flow of an application if there's always a chance a promise could later be caught?
I understand promises are typically for asynchronous code, but one could imagine a promise which may return immediately with a validation error or something.
I am aware of try/catch, I'm just trying to understand how this works.
Running in Microsoft Edge 91.0.864.59 (64-bit)
If you're just talking about a message you see in the console that then disappears, then this is just something that occurs in a specific Javascript environment where it decides after-the-fact to rescind/remove a debug message in the console. It does not affect the running of your code in any way. Try three different browsers and you will see different behaviors in each because what shows in the console and when it shows there is up to the implementor of the engine/console, not something that is standardized or something that affects the outcome of your code.
Beyond that, let's discuss issues related to the timing of when a .catch() handler is added. The rejection of your promise is not processed synchronously. The promise state is changed immediately internal to the promise, but it does not synchronously call any .catch() handlers. Instead, a job is inserted into the promise job queue and only when the current chunk of Javascript that is executing is finished and returns control back to the event loop does the promise job get to do its work.
So, in your code, the .catch() handler is added in the current chunk of Javascript execution BEFORE the promise tries to call its catch handlers. Thus, when the promise job that contains the rejection does actually get processed, it is not an uncaught promise because the .catch() handler is already in place.
FYI, typing code into the console and executing it there will not necessarily offer the same timing (and thus errors) as running code in a real script. My comments above are about what happens if you run your whole script at once. I always evaluate asynchronous code in a real execution environment running a complete script, not by typing code into a console and running it pieces at a time.
I can even type in the first half, hit enter, see the error, then add the catch and the error goes away.
That's just a weirdness that occurs in the console when you run pieces of code, but not the whole script. That is not representative of running the whole code in a script.
This seems weird to me: how can JavaScript know that a promise will eventually be caught?
It doesn't. It evaluates whether there's a .catch() handler when the rejection is actually processed (which is via the Promise job queue).
How would this affect control flow of an application if there's always a chance a promise could later be caught?
It's not really an issue because the .catch() handler just needs to be in place before control returns to the event loop when the promise is actually rejected. And, that is usually how code is written so this isn't an issue. You create the promise (or call a function that returns a promise), then you add handlers to it - all in one body of code.
I understand promises are typically for asynchronous code, but one could imagine a promise which may return immediately with a validation error or something.
Neither .then() or .catch() handlers are ever called synchronously, even if the promise is resolved or rejected synchronously. They are always called via the Promise job queue which is always after the current synchronously running Javascript finishes executing and returns control back to the event loop.
I'm looking for information about whether promises chains are guaranteed to be completed before being garbage collected if the reference is lost. I'm executing a call to my API within a React useEffect hook, but am not clear about what happens if one of the dependencies change and the useEffect hook is re-executed while the promise is still pending.
A contrived example:
const useApiFetch = (query, myVariable) => {
const client = useClient();
useEffect(()=>{
client
.query(query)
.then(() => console.log('inside thenable'))
.catch(() => console.log('inside catch');
},[variable, client]);
}
Questions:
What happens if myVariable changes and useEffect is re-executed while the promise is pending? Will the promise complete before being garbage collected?
What about if the component that is consuming this hook is re-rendered or removed from the virtual DOM?
If I don't have a cleanup function, is there any chance of a memory leak?
I may not be the perfect one but from my usage with react and functional components i will try to answer these.
When your dependencies for the hook changes, the function inside is re invoked. That means in your case a second api call. What this in turn results in may depend on api response time, client device, internet speed and way you handle these.
For example, if you are rendering something in the promise success for example a Text. What happens is once you UI renders the first data that it gets, like if you are setting state or something. Aa soon as the next api call resolves the UI again re-renders to reflect that change.
taking your example code. If you change the dependency "variable" three times, you get three "inside thenable" if promise resolves or else the catch console.log.
Note: If for any reason you api process takes some time for example a large query, then you may not be able to tell which api call will get resolved first. I had this issue when i implemented a text based onChange search using api. A good solution will be to debounce your api calls to limit no of calls and also to cancel unwanted calls.
If you have setStates that is tied to your promise resolution/rejection and you haven't properly handled the unmount condition. React will show you a warning stating the same. Something that means "You have a state change happening in an unmounted component/screen". This can lead to memory leaks if left unchecked.
Hope you get some point out of this. I am not a pro at this, these are somethings that i found while working with React.
The other day I came across the following peace of code:
let promise = Promise.reject(new Error('Promise Failed!')); // 1
setTimeout(() => promise.catch(err => alert('caught')), 1000); // 2
// 3
So I was quite surprised to find out the error was caught in the handler registered one second after the error had occurred.
Now, let me explain step by step the way I perceive this, so you could correct me and tell me where I'm wrong:
During the current macrotask, which is executing this whole script, a promise rejects (1) and since there is no registered rejection handler the control sequentially moves on to the next line.
We call setTimeout passing it a callback function where rejection handler is to be registered (2). But this callback will be scheduled only after a second delay. Furthermore, it will be executed within a separate task.
The execution is on line 3. The current macrotask is done, both stack and task queue are empty. All that's left is to wait for a given delay of 1 second.
One second timeout is complete. Our callback gets to the task queue and right away it is being pushed to the stack which effectively runs it.
During the execution of the callback function, which is part of a new task, .catch handler is registered for the promise rejected one second ago, which took place during the previous and already finished task. Nevertheless, it successfully catches the error.
Does this mean that all this time the error had been somewhere in memory waiting for a chance that maybe .catch handler would be registered later? But what if it had never happened and the number of rejected promises had been a lot more? Will unhandled errors remain to 'hang' in memory waiting for its handler to be registered?
Does this mean that all this time the error had been somewhere in memory waiting for a chance that maybe .catch handler would be registered later?
Yes, it is stored in the promise object. Notice that promises are not just a notification mechanism, they are meant to represent the result of a one-off asynchronous task. The fulfillment value or rejection reason (in your case, the Error instance) and the resolution state are stored on the promise when it settles.
The main idea behind this is that a .then or .catch handler will always be called with the result value, regardless whether the .then() call happens before or after the settling of the promise. This also allows multiple handlers.
Will unhandled errors remain to 'hang' in memory waiting for its handler to be registered?
If you didn't have the setTimeout, the let promise variable and with it the promise object and the error object would have been garbage-collected immediately.
to answer your question directly:
Does this mean that all this time the error had been somewhere in memory waiting for a chance that maybe .catch handler would be registered later?
Yes, we have a WeakMap of pending rejections that keeps track of promises that were rejected but not synchronously handled. Chrome does something similar (I can link to it if you want).
But what if it had never happened and the number of rejected promises had been a lot more?
The assumption in the design of these features is that rejections are pretty rare and are for exceptional cases - so the rejection path is kind of slow. But yes, you could theoretically create a lot of "pending" rejections.
Will unhandled errors remain to 'hang' in memory waiting for its handler to be registered?
We only wait for a microtick - so the flow is:
You create a rejected promise
It has no handler, so it gets put in the WeakMap
All microtasks are run (process.nextTick/Promise.resolve.then)
If it is still rejected, it is an unhandled rejection and it gets logged to the screen / the event is fired.
That is, the rejection is not kept "in memory" for 1000ms (the timer duration) but just for as long as it takes for microtasks to run.
Coming from a python async background, in python it's very important to always keep track of async tasks (promises). The runtime gives errors for "floating" async tasks with no references. In Javascript, though, in some cases it seems perfectly OK to just start an async task and not await it or even remember its Promise, for instance if you don't care about its return value and just want it to get executed "later".
Are there guidelines or best practices about when in JS/TS, either in browser or in node.js, it's acceptable to just let an async task go, and not keep a reference to it? Clearly if you care about its return value in the mainline code you have to await it, and similarly if you care that errors are reported before the main function completes. What are other cases and considerations?
To be clear I'm not asking about opinions, I'm asking what are the important things to keep in mind when "firing and forgetting" async tasks (if that's the right term).
Whether and when to do this will be a matter of opinion, so that part is off-topic for Stack Overflow.
But the concrete part is the question of: if you do this, are there any precautions you have to take? And the answer there is: Yes. You need to catch and handle errors.
Consider this fire-and-forget async call that returns a promise:
doSomethingAsync();
If the thing being done can fail, that promise can be rejected. If it's rejected and nothing handles that rejection, then:
In a browser, you get an error written to the browser console. 99.9999999% of users won't notice.
In Node.js, you get this error written to the console:
(node:26477) [DEP0018] DeprecationWarning: Unhandled promise rejections are deprecated. In the future, promise rejections that are not handled will terminate the Node.js process with a non-zero exit code.
Note that warning: At some point, a future version of Node.js may start *terminating the process when you allow a promise rejection to go unhandled.
So if you fire-and-forget, be sure to catch errors, even if you just silently swallow them:
doSomethingAsync().catch(error => {}); // Obviously only do this if you really don't
// care about the error!
Fun side note: In JavaScript, you have unhandled promise fulfillment all the time. Consider:
doSomethingAsync()
.then(result => {
// ...do something with result...
})
.catch(error => {
// ...handle/report the error...
});
Are all the promises handled there?
No. And that's just fine.
Remember that then and catch create and return a promise. The promise returned by catch in the above has no handlers attached to it. That's fine, provided it is only ever fulfilled, not rejected. Nothing ever does anything with that fulfillment. :-)
I think the general concern I would have is, do you have a mechanism to handle errors?
eslint has a no-floating-promises rule that at the very least forces you to add a .catch(), which I think is good.
I'm using Promises in Angular (4) project and I have a question about them that I couldn't find a response to it in the docs.
When I create a Promise, I basically wait for an async answer from a service/party. But how long should I expect this Promise to stay in pending state?
Is there any mechanism that will terminate it after a while?
How reliable is this concept of waiting/pending?
Let's suppose that I need to get some data from a busy service that can answer even after few minutes of waiting, maybe more, no matter if the computing of the response is a resource intensive process or that service is linked with another one that is responding very slow.
Is there anything on the client side that will somehow terminate my Promise and determine/force to create another one to ask again for my data?
Someone suggested to upgrade to Observables, and I will do that, but for now I want to keep using Promises, at least for some areas of the code.
Tks a lot
A Promise can be in pending state as long as the page is loaded.
You can wrap the call in another Promise where you introduce a timeout like shown in
let wrappingPromise = new Promise((resolve, reject) => {
var error = false;
setTimeout(function(){
reject("some error");
}, 3000);
this.http.get(...).toPromise().then(res => {
if(!error) {
resolve(res.json);
}
});
});
This will cause an error when the timeout is reached.
It will still wait to receive the full response.
An Observable might be able to forward a cancellation and close the connection, so that the result isn't even received anymore when the timeout is reached. This might depend on whether the concrete implementation and the browser used browser API supports that.
new Promise(() => {}) will never settle, like a callback never called.
A promise is a return object you attach callbacks to, instead of passing callbacks into the function. That's all. It is not a control surface of the asynchronous operation that was just started.
Instead, look to the asynchronous API you called for such controls, if it has them.
Creating promises
Most people are consumers of promises returned from asynchronous APIs. There's no reason to create a Promise other than to wrap a legacy callback API. In an ideal world there'd be no need.