Proposal: Allow Promise callbacks to be removed

Naveen Chawla naveen.chwl at gmail.com
Tue Apr 24 11:15:56 UTC 2018


leads* to better predictability

On Tue, 24 Apr 2018, 4:44 pm Naveen Chawla, <naveen.chwl at gmail.com> wrote:

> 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/b513a059/attachment-0001.html>


More information about the es-discuss mailing list