How long a Promise can remain in pending state? - javascript

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.

Related

does Javascript .then() function check for the promise in a web api

Thank you for reading this.
I'm really struggling with fully understanding how async programming works in Javascript.
Imagine this code:
Promise.resolve().then(randomcallback)
So in this scenario what I think would happen is the following: Firstly, in the normal call stack the Promise.resolve() function would immediately resolve a Promise object and return it. Then, the .then() function checks if the promise is resolved and if so it adds the randomcallback function to the microtask queue which when the call stack is empty gets executed and returns a new promise object.
But imagine if instead of calling the .then() function on a resolved promise object, we call it on a pending object. How is the then function able to check when the Promise object is resolved? Is it constantly checking in some web api thread like some event listener (since the rest of the program can run on) for the status property to be changed to fulfilled to then pass the callback function to the microtask queue?
The same for async await, does the await keyword just contact some web api to listen when the promise gets resolved? If so does it still do that if the promise object is already resolved at the beginning (no settimeout in it)
if you use:
randomunresolvedpromise.then(randomcallback)
let's say that randomunresolvedpromise gets fulfilled in 3 seconds (when callstack is empty)
how is the .then function even executed (also I've looked at the source code of the library, but it's really confusing since it uses so many callback functions) since the program literally keeps running and randomresolvedpromise only gets fulfilled when the stack is empty, is the function also just waiting in some sort of web api or what?
I've literally watched a lot of videos, read a lot of articles, but they don't seem to touch what really goes on in the event loop when using Promises and async and await.
It's all really confusing to me, I hope my question really makes any sense.
I really appreciate any answer, thanks!
How is the then function able to check when the Promise object is resolved?
It doesn't. The then method puts the callback on the promise's list of fulfillment callbacks, and then its job is complete. Later, when the promise is fulfilled, it's that code (fulfilling the promise) that puts calls to the promise's fulfillment callbacks in the microtask queue. (You're right though that it's then that does it when the promise is already fulfilled when then is called.)
await works the same way, since await is "just" syntactic sugar for then. (That "just" skips over a lot of details, though. :-D )
Side note: You'll notice I said "fulfillment callbacks," not "resolution callbacks," and referred to the promise being fulfilled (not resolved). There's a big difference between "fulfilled" and "resolved," but unfortunately it's not well understood and people use "resolve" where they mean "fulfill" a lot. See my blog post here (which the MDN folks linked from their documentation) for the difference (and why it matters). (That said, if you say you "resolve the promise with X" and X isn't a promise or other thenable, that's basically synonymous with "fulfill the promise with X." That's not true when X is a promise or other thenable.)

How to cause fetch calls to queue up once I've reached max number of concurrent calls?

I have code that will make a number of concurrent calls to a piece of software that provides a rest interface to query our system. We recently discovered that a sufficiently high number of concurrent calls (somewhere between 30 to 100) can cause the software to fail and actually break the backed functionality.
As a work around I'd like to fix this by having my code block if more then X concurrent calls are outstanding, sending new calls only after the old calls resolve. As I already have a number of outstanding UIs I'd also prefer to change this behavior by updating a shared lower level Request method.
My 'request' method returns a promise, which is ultimately called within a redux Saga in each UI via the 'call' redux-effect. This means I need to be able to return a promise from my Request method, which means figuring some way to return a promise that won't try to resolve a Request until the earlier Requests resolve.
I started to write my own, but there is no easy way for me to 'alert' a promise, to tell it that previous promises have completed. The best way to roll my own I was able to figure out involved using Promise.race and then having the exact same callNextQueued method called for both the resolve and reject method, and generally it just feels kind of ugly conceptually.
Is there a better way to implement this? either an API that already does it, or a clean way to work with promises to role my own logic for starting a new request whenever any old request completes?

Bluebird promise usage, does it leak?

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.

angularJS $q -- stopping execution to wait for all promises

I've got this problem that I couldn't find a solution for by googling.
I've got a library, that I'm using (and do not want to edit, unless it's really necessary) that allows the user to select an item, then calls my custom callback function to modify the item and then continues working with it.
I need to perform some asynchronous tasks on it, which may take some time. This creates a race condition as my async tasks have not yet finished when the callback function is finished and the library continues its work on the item.
library.onItemSelectionCallback = function (item) {
myService.modifyItem(item).then(
function (modifiedItemProperty) {
item.newProperty = modifiedItemProperty;
});
myService.anotherModifyItem(item).then(
function (modifiedItemProperty) {
item.existingProperty = modifiedItemProperty;
});
}
How do I wait for both of my async tasks to finish, before allowing this callback to finish?
Only thing I could think of is looping with while and sleep every hundred or so milliseconds until both of the promises have been resolved, but that doesn't seem to be a very good solution.
I understand that this makes async requests quite synchronous and might possibly be detrimental for UX, but do not really see another way out.
EDIT: I know that i'm risking with removing the generic nature of the question and thus making it too localized, I will say that I'm trying to use angular-file-upload module, specifically, trying to mount a custom imageService, that would resize the picture before it's upload. I'm mounting it on the onBeforeUploadItem callback. The idea is that creating the resized image may take a while and that is why I need to return a promise from my imageService, that needs to be resolved before upload.
If modifyItem and anotherModifyItem work independently (that is, one does not rely on the other), you can just pipe them both into $q.all, eg
library.onItemSelectionCallback = function(item) {
var promises = {
newProperty: myService.modifyItem(item),
existingProperty: myService.anotherModifyItem(item)
};
return $q.all(promises).then(function(values) {
return angular.extend(item, values);
});
}
This will return a promise that resolves with item.
For the first part of my question -- Yes, I guess the only way to really wait for those two promises to be resolved would be something with a while and sleep, making them synchronous, which would probably work and not even be that bad (except for the site pausing until the requests are fulfilled), but would make me feel very, very bad about myself as a person and how my actions affect this world.
It is not possible to correctly mix callbacks and promises without hacks afaik.
For the second part of my question -- as per comments of #georgeawg, figured that an AngularJS module that implements HTML5 API and callbacks instead of $http service and promises is not how a good AngularJS module should be implemented, and so I moved towards a different module ng-file-upload, which, even though one could argue is less stylish, does the job very well and in an Angular way (ng-file-upload provides a simple $upload service, that returns a promise. If you want to modify files before upload, suggested way is to simply $watch and catch the moment user drag-drops or selects a file.).

Inconsistent interplay between IndexedDB transactions and Promises

I saw sync-promise posted on Reddit and got into a discussion with the author. We noticed some weird inconsistencies in the relationship between IndexedDB transactions and promises.
IndexedDB transactions auto-commit when all the onsuccess events finish. One complication is that you can't do anything asynchronous inside an onsuccess callback except do another operation on the same transaction. For example, you can't start an AJAX request in an onsuccess and then reuse the same transaction after the AJAX request returns some data.
What do promises have to do with it? As I understand it, promise resolution is supposed to always be asynchronous. This would imply that you can't use promises without auto-committing an IndexedDB transaction.
Here is an example of what I'm talking about:
var openRequest = indexedDB.open("library");
openRequest.onupgradeneeded = function() {
// The database did not previously exist, so create object stores and indexes.
var db = openRequest.result;
var store = db.createObjectStore("books", {keyPath: "isbn"});
var titleIndex = store.createIndex("by_title", "title", {unique: true});
var authorIndex = store.createIndex("by_author", "author");
// Populate with initial data.
store.put({title: "Quarry Memories", author: "Fred", isbn: 123456});
store.put({title: "Water Buffaloes", author: "Fred", isbn: 234567});
store.put({title: "Bedrock Nights", author: "Barney", isbn: 345678});
};
function getByTitle(tx, title) {
return new Promise(function(resolve, reject) {
var store = tx.objectStore("books");
var index = store.index("by_title");
var request = index.get("Bedrock Nights");
request.onsuccess = function() {
var matching = request.result;
if (matching !== undefined) {
// A match was found.
resolve(matching);
} else {
// No match was found.
console.log('no match found');
}
};
});
}
openRequest.onsuccess = function() {
var db = openRequest.result;
var tx = db.transaction("books", "readonly");
getByTitle(tx, "Bedrock Nights").then(function(book) {
console.log('First book', book.isbn, book.title, book.author);
return getByTitle(tx, "Quarry Memories");
}).then(function(book) {
console.log('Second book', book.isbn, book.title, book.author);
// With native promises this gives the error:
// InvalidStateError: An attempt was made to use an object that is not, or is no longer, usable
// With bluebird everything is fine
});
};
(Full disclosure: demo was created by paldepind, not me!)
I've tried it in Chrome and Firefox. It fails in Firefox due to the transaction auto-committing, but it actually works in Chrome! Which behavior is correct? And if Firefox's behavior is correct, is it literally impossible to use "correct" promise implementations with IndexedDB transactions?
Another complication: If I load bluebird before running the above demo, it works in both Chrome and Firefox. Does this imply that bluebird is resolving promises synchronously? I thought it wasn't supposed to do that!
JSFiddle
This is probably due to the difference between microtasks and tasks ("macrotasks"). Firefox has never had a standards-complaint promise implementation that uses microtasks, whereas Chrome, Bluebird, and others correctly use microtasks. You can see this in how a microtask (which executes "sooner" than a macrotask, but still async) falls inside the transaction boundary, whereas a macrotask (e.g. from Firefox's promises) does not.
So, this is a Firefox bug.
Ok, so I've once again taken a deep dive into the IndexedDB, the DOM and the HTML specification. I really need to get this right for SyncedDB since it relies heavily on promises inside transactions.
The crux of the problem is whether or not the delayed execution of the onFulfilled and the onRejected callbacks to then that Promises/A+ compliant must exhibit will trigger an IndexedDB transaction commit.
The IndexedDB rules for a transactions lifetime are actually pretty straight forward when you extract them from the specification and line them up:
Request can only made against a transaction when its active flag is set to true (as specified here).
When a transaction is created it is initially active until control is returned to the the browsers event loop (this is specified in the transaction creation steps).
Every time a success or error event is fired, the transactions active flag is set to true before as the last step before the event is dispatched. After the event dispatch the transaction is flagged as inactive again (this is specified in the steps for firing a success/error event.
When a transaction can no longer become active it will automatically be committed (as specified here).
This roughly translates to:
When you create a transaction you can place as many request against it as you wish.
From then on new request can only be made inside event handlers for another requests success or error event listener.
When all requests has been executed and no new requests placed the transaction will commit.
The question then becomes: If a promise is fulfilled inside a request's success or error event listener will its onFulfilled callbacks be invoked before the IndexedDB sets the transaction as inactive again? I.e. will onFullfilled callbacks be called as part of step 3 in firing a success event?
The step dispatches an event and IndexedDB uses DOM events so the actual operation performed is beyond the IndexedDB specification. The steps for dispatching an event is, instead, specified here in the DOM specification. Going over the steps it becomes clear that at no point is a microtask (which would call the promise callbacks) checkpoint performed. So the initial conclusion is that the transaction will be closed before any onFulfilled callbacks will be invoked.
However, if we attach the event listeners by specifying an onsuccess attribute on the request object things gets more hairy. In that case we are not simply adding an event listener as per the DOM specification. We are instead setting an event handler IDL attribute as defined in the HTML specification.
When we do that the callback is not added directly to the list of event listeners. It is instead "wrapped" inside the the event handlers processing algorithm. This algorithm performs the following important operations:
In step 3 it runs the jump to code entry-point algorithm.
This then performs the steps to clean up after running a callback which
Finally, this performs a microtask checkpoint. Which means that your promise callbacks will be invoked before the transaction is marked as inactive! Hurrah!
This is good news! But it is weird how the answer depends on whether or not you listen for the success event by using addEventListener or set a onsuccess event handler. If you do the former the transaction should be inactive when your promise's onFulfilled callbacks is invoked and if you do the later it should still be active.
I was, however not able to reproduce the difference in existing browsers. With native promises Firefox fails at the example code no matter what and Chrome succeeds even when using addEventListener. It is possible that I've overlooked or misunderstood something in the specifications.
As a final note Bluebird promises will close transactions in Internet Explorer 11. This is due to the scheduling that Bluebird uses in IE. My synchronized promise implementation works inside transactions in IE.
You are correct: Promises are resolved asynchronously, and IndexedDB has some synchronous requirements. While other answers point out that native promises may work correctly with IndexedDB in certain versions of certain browsers, it is likely as a practical matter that you will have to deal with the issue of it not working in some of the browsers you're targeting.
Using a synchronous promise implementation instead, however, is a horrible idea. Promises are asynchronous for very good reasons, and you are introducing needless chaos and potential for bugs if you make them synchronous instead.
There is a fairly straightforward workaround, however: use a Promise library that provides a way to explicitly flush its callback queue, and an IndexedDB wrapper that flushes the promise callback queue after invoking event callbacks.
From the Promises/A+ point of view, there isn't any difference between the handlers being called at the end of the event, or at the beginning of the next tick cycle -- they are still being called after all the code that set up the callbacks has finished, which is the important part of Promise asynchrony.
This allows you to use promises that are asynchronous, in the sense of meeting all the Promises/A+ guarantees, but which still ensure that the IndexedDB transaction isn't closed. So you still get all the benefits of callbacks not happening "all at once".
The catch of course is that you need libraries that support this, and not every Promise implementation exposes a way to specify a scheduler or to flush its callback queue. Likewise, I'm not aware of any open source IndexedDB wrappers that have support for this.
If you are writing your own IndexedDB wrapper with Promsies, though, it would be good for it to use an appropriate Promise implementation, and flush its callback queue accordingly. One easy option would be to embed one of the many "micropromise" implementations that are only 100 lines or so of Javascript, and modify it as needed. Alternately, using one of the larger mainstream Promise libraries with custom scheduling support would be doable.
Do not use a synchronous promise library, the synchronous Bluebird build, or a synchronous scheduler. If you do that, you might as well abandon promises altogether and use straight callbacks.
Follow-up note: one commenter suggests that a synchronous promise is as safe as flushing a callback queue. But they are wrong. Horribly, horribly wrong. You can reason about a single event handler well enough to say "there isn't any other code running here; it's okay to invoke the callbacks now". To make a similar analysis with synchronous promises requires a complete understanding of how everything calls everything else... which is precisely opposite from the reason you want promises in the first place.
In the specific sync-promise implementation, the sync-promise author claims that their promise library is now "safe", and doesn't "release Zalgo". They are once again wrong: it isn't safe, and does release Zalgo. The author apparently did not actually understand the article about "releasing Zalgo", and has successfully reimplemented jQuery promises, which are widely considered horribly broken for a number of reasons, including their Zalgo-ness.
Synchronous promises are simply not safe, no matter your implementation.

Categories