Proposal: Allow Promise callbacks to be removed
Naveen Chawla
naveen.chwl at gmail.com
Tue Apr 24 11:14:57 UTC 2018
Having promises be non-breakable I think is an advantage. I think it least
to better predictability without having to check if another part of the
code may have "broken" it. So I tend to prefer callbacks explicitly
handling expiry in application logic, rather than allowing promises to be
permanently broken in terms of their originally declared resolution
behaviour. They are "promises" after all.
On Tue, 24 Apr 2018 at 16:12 kai zhu <kaizhu256 at gmail.com> wrote:
> I see a simple scenario like the following one:
>
> - user asks for a very expensive task clicking section A
> - while it's waiting for it, user changes idea clicking section B
> - both section A and section B needs that very expensive async call
> - drop "going to section A" info and put "go to section B" to that
> very same promise
> - whenever resolved, do that action
>
> A caching mechanism to trigger only once such expensive operation would
> also work, yet it's not possible to drop "go into A" and put "go into B”
>
>
> for your scenario, what you want is a cacheable background-task, where you
> can piggyback B onto the task initiated by A (e.g. common-but-expensive
> database-queries that might take 10-60 seconds to execute).
>
> its generally more trouble than its worth to micromanage such tasks with
> removeListeners or make them cancellable (maybe later on C wants to
> piggyback, even tho A and B are no longer interested). its easier
> implementation-wise to have the background-task run its course and save it
> to cache, and just have A ignore the results. the logic is that because
> this common-but-expensive task was recently called, it will likely be
> called again in the near-future, so let it run its course and cache the
> result.
>
> here's a real-world cacheable-task implementation for such a scenario, but
> it piggybacks the expensive gzipping of commonly-requested files, instead
> of database-queries [1] [2]
>
> [1]
> https://github.com/kaizhu256/node-utility2/blob/2018.1.13/lib.utility2.js#L4372
> - piggyback gzipping of files onto a cacheable-task
> [2]
> https://github.com/kaizhu256/node-utility2/blob/2018.1.13/lib.utility2.js#L5872
> - cacheable-task source-code
>
>
>
> ```js
> /*jslint
> bitwise: true,
> browser: true,
> maxerr: 4,
> maxlen: 100,
> node: true,
> nomen: true,
> regexp: true,
> stupid: true
> */
>
> local.middlewareAssetsCached = function (request, response,
> nextMiddleware) {
> /*
> * this function will run the middleware that will serve cached
> gzipped-assets
> * 1. if cache-hit for the gzipped-asset, then immediately serve it to
> response
> * 2. run background-task (if not already) to re-gzip the asset and update
> cache
> * 3. save re-gzipped-asset to cache
> * 4. if cache-miss, then piggy-back onto the background-task
> */
> var options;
> options = {};
> local.onNext(options, function (error, data) {
> options.result = options.result ||
> local.assetsDict[request.urlParsed.pathname];
> if (options.result === undefined) {
> nextMiddleware(error);
> return;
> }
> switch (options.modeNext) {
> case 1:
> // skip gzip
> if (response.headersSent ||
>
> !(/\bgzip\b/).test(request.headers['accept-encoding'])) {
> options.modeNext += 1;
> options.onNext();
> return;
> }
> // gzip and cache result
> local.taskCreateCached({
> cacheDict: 'middlewareAssetsCachedGzip',
> key: request.urlParsed.pathname
> }, function (onError) {
> local.zlib.gzip(options.result, function (error, data) {
> onError(error, !error && data.toString('base64'));
> });
> }, options.onNext);
> break;
> case 2:
> // set gzip header
> options.result = local.base64ToBuffer(data);
> response.setHeader('Content-Encoding', 'gzip');
> response.setHeader('Content-Length', options.result.length);
> options.onNext();
> break;
> case 3:
> local.middlewareCacheControlLastModified(request, response,
> options.onNext);
> break;
> case 4:
> response.end(options.result);
> break;
> }
> });
> options.modeNext = 0;
> options.onNext();
> };
>
> ...
>
> local.taskCreateCached = function (options, onTask, onError) {
> /*
> * this function will
> * 1. if cache-hit, then call onError with cacheValue
> * 2. run onTask in background to update cache
> * 3. save onTask's result to cache
> * 4. if cache-miss, then call onError with onTask's result
> */
> local.onNext(options, function (error, data) {
> switch (options.modeNext) {
> // 1. if cache-hit, then call onError with cacheValue
> case 1:
> // read cacheValue from memory-cache
> local.cacheDict[options.cacheDict] =
> local.cacheDict[options.cacheDict] ||
> {};
> options.cacheValue =
> local.cacheDict[options.cacheDict][options.key];
> if (options.cacheValue) {
> // call onError with cacheValue
> options.modeCacheHit = true;
> onError(null, JSON.parse(options.cacheValue));
> if (!options.modeCacheUpdate) {
> break;
> }
> }
> // run background-task with lower priority for cache-hit
> setTimeout(options.onNext, options.modeCacheHit &&
> options.cacheTtl);
> break;
> // 2. run onTask in background to update cache
> case 2:
> local.taskCreate(options, onTask, options.onNext);
> break;
> default:
> // 3. save onTask's result to cache
> // JSON.stringify data to prevent side-effects on cache
> options.cacheValue = JSON.stringify(data);
> if (!error && options.cacheValue) {
> local.cacheDict[options.cacheDict][options.key] =
> options.cacheValue;
> }
> // 4. if cache-miss, then call onError with onTask's result
> if (!options.modeCacheHit) {
> onError(error, options.cacheValue &&
> JSON.parse(options.cacheValue));
> }
> (options.onCacheWrite || local.nop)();
> break;
> }
> });
> options.modeNext = 0;
> options.onNext();
> };
> ```
>
> On 24 Apr 2018, at 6:06 PM, Oliver Dunk <oliver at oliverdunk.com> wrote:
>
> Based on feedback, I agree that a blanket `Promise.prototype.clear()` is a
> bad idea. I don’t think that is worth pursuing.
>
> I still think that there is value in this, especially the adding and
> removing of listeners you have reference to as Andrea’s PoC shows.
> Listeners would prevent the chaining issue or alternatively I think it
> would definitely be possible to decide on intuitive behaviour with the
> clear mechanic. The benefit of `clear(reference)` over listeners is that it
> adds less to the semantics.
>
> I think the proposed userland solutions are bigger than I would want for
> something that I believe should be available by default, but I respect that
> a lot of the people in this conversation are in a better position to make a
> judgement about that than me.
> _______________________________________________
> es-discuss mailing list
> es-discuss at mozilla.org
> https://mail.mozilla.org/listinfo/es-discuss
>
> _______________________________________________
> es-discuss mailing list
> es-discuss at mozilla.org
> https://mail.mozilla.org/listinfo/es-discuss
>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://mail.mozilla.org/pipermail/es-discuss/attachments/20180424/814e6eef/attachment-0001.html>
-------------- next part --------------
A non-text attachment was scrubbed...
Name: Screen Shot 2018-04-24 at 5.31.58 PM copy.jpg
Type: image/jpeg
Size: 68324 bytes
Desc: not available
URL: <http://mail.mozilla.org/pipermail/es-discuss/attachments/20180424/814e6eef/attachment-0001.jpg>
More information about the es-discuss
mailing list