The problem with the done () function is that people forget to call it, causing leaks.
I like Bergi's answer with the transferred callback because it is clean, but it is not a very “promise-y,” and it is still openly unsafe, for example. if people cling to promises that never allow the promise of a callback, then it rushes and flows.
This is a problem discussed in the browser APIs, and one template we are thinking about is returning
The AutoClosingPromise function acts like a promise, but does two things differently:
It "closes the ticket" (completed calls) after executing its .then ().
Also, when he assumes another promise, if he sees another AutoClosingPromise returned from his .then (), then he forwards it (passes this promising ticket - another ticket) to AutoClosingPromise, which he returned from his own .then () )
The first part means that the API can return AutoClosingPromise with a “ticket” that contains an open resource (for example, an open counter), and be sure that the ticket will be closed after the first .then () function returns.
The second part allows the caller to make additional asynchronous calls to the API from the direct .then () function, allowing the API to keep the resource open as long as tickets overlap in time.
A feature of this is that resources are not served through regular promises, only with AutoClosing, avoiding the risk of leaks. For instance:
var lock = new ExampleLock(); lock.access("foo") .then(() => lock.set("foo1")) .then(() => lock.set("foo2")) .then(() => lock.set("foo3")) .then(() => {}) .then(() => lock.set("foo4")) .catch(failed);
will expand the resource (lock) to the first three, but not the fourth:
setting foo1 [LOCKED] setting foo2 [LOCKED] setting foo3 [LOCKED] setting foo4 [UNLOCKED]
Here is the code:
function AutoClosingPromise(ticket, p) { this.pending = true; this.ticket = ticket; var close = result => { this.pending = false; if (this.ticket) { this.ticket.close(); if (result && result.handoffTicket && this.returnedThenPromise) { // callback returned an AutoClosingPromise! Forward its ticket this.returnedThenPromise.takeTicket(result.handoffTicket()); } } return result; }; this.p = p.then(v => close(this.success && this.success(v)), r => close(this.failure && this.failure(r))); } AutoClosingPromise.prototype = { then: function(success, failure) { if (this.pending && !this.success && !this.failure) { this.success = success; this.failure = failure; this.returnedThenPromise = new AutoClosingPromise(null, this.p); return this.returnedThenPromise; } else { return this.p.then(success, failure); } }, takeTicket: function(ticket) { this.ticket = ticket; }, handoffTicket: function() { var ticket = this.ticket; this.ticket = null; return ticket; } };
and a script: http://jsfiddle.net/jib1/w0ufvahL (you need a browser that understands the functions of es6 arrows, for example Firefox, for example, not Chrome).
Since the API manages all the asynchronous calls that issue tickets, this should be enough of a leak. For instance. even if the caller ignores the promise returned from the API in full, close is still called.
Please note that this is a fairly new idea, not a proven design, so if you use it, let me know how it works .; -)