Proposal: defer keyword

kai zhu kaizhu256 at gmail.com
Mon Sep 24 07:12:54 UTC 2018


how would you handle cleanup after timeouts in https://github.com/tc39/proposal-using-statement? <https://github.com/tc39/proposal-using-statement?>

when managing io-complexity at the integration-level, i feel the reliable/zero-magic/verifiable way to guarantee cleanup in all scenarios is still to use traditional-callbacks, with a timeout-closure.

here are 2 examples using the recursive-callback approach, where cleanup is guaranteed-to-run in the “default” switch-statement; one is real-world [1], and the other is this working, standalone nodejs code:

[1] real-world example of guaranteed cleanup
https://github.com/kaizhu256/node-utility2/blob/2018.9.8/lib.utility2.js#L3320 <https://github.com/kaizhu256/node-utility2/blob/2018.9.8/lib.utility2.js#L3320>



```js
// example.js
// fully-working standalone code to fetch data from https://api.github.com <https://api.github.com/>
// and cleanup afterwards
/*jslint node: true*/
/*property
    GITHUB_TOKEN, authorization, concat, created_at, date, destroy, end, env,
    error, exit, headers, html_url, log, map, on, parse, push, request,
    responseHeaders, responseStatusCode, size, slice, stargazers_count, status,
    statusCode, stringify, updated_at
*/
(function () {
    "use strict";
    var cleanup;
    var chunkList;
    var modeNext;
    var onNext;
    var request;
    var response;
    var timerTimeout;
    cleanup = function () {
        // cleanup timerTimeout
        clearTimeout(timerTimeout);
        // cleanup request-stream
        try {
            request.destroy();
        } catch (ignore) {
        }
        // cleanup response-stream
        try {
            response.destroy();
        } catch (ignore) {
        }
        console.error(
            "\u001b[31mcleaned up request and response streams\u001b[39m"
        );
    };
    onNext = function (error, data) {
        try {
            // guarantee cleanup on callback-error
            if (error) {
                console.error(error);
                modeNext = Infinity;
            }
            modeNext += 1;
            switch (modeNext) {
            case 1:
                // guarantee cleanup after 150000ms timeout
                timerTimeout = setTimeout(function () {
                    onNext(new Error("timeout error"));
                }, 15000);
                // fetch json repository-data from
                // https://api.github.com/tc39/repos
                request = require("url").parse(
                    "https://api.github.com/orgs/tc39/repos"
                );
                request.headers = {};
                request.headers["user-agent"] = "undefined";
                // add optional oauth-token to increase rate-limit quota
                if (process.env.GITHUB_TOKEN) {
                    request.headers.authorization = (
                        "token " + process.env.GITHUB_TOKEN
                    );
                }
                request = require("https").request(
                    request,
                    // concatenate data-chunks from response-stream
                    function (_response) {
                        chunkList = [];
                        response = _response;
                        response
                        .on("data", function (chunk) {
                            chunkList.push(chunk);
                        })
                        .on("end", function () {
                            onNext(null, Buffer.concat(chunkList));
                        })
                        // guarantee cleanup on response-error
                        .on("error", onNext);
                    }
                );
                // guarantee cleanup on request-error
                request.on("error", onNext)
                .end();
                break;
            case 2:
                // print response data
                console.log(JSON.stringify(
                    {
                        responseStatusCode: response.statusCode,
                        responseHeaders: {
                            date: response.headers.date,
                            status: response.headers.status,
                            "content-type": response.headers["content-type"],
                            "content-length": (
                                response.headers["content-length"]
                            )
                        }
                    },
                    null,
                    4
                ));
                // print first 5 results of abridged,
                // JSON.parsed concatenated-data
                console.log(JSON.stringify(
                    JSON.parse(String(data))
                    .slice(0, 5)
                    .map(function (githubRepo) {
                        return {
                            html_url: githubRepo.html_url,
                            created_at: githubRepo.created_at,
                            updated_at: githubRepo.updated_at,
                            size: githubRepo.size,
                            stargazers_count: githubRepo.stargazers_count
                        };
                    }),
                    null,
                    4	
                ));
                // guarantee cleanup on successful-operation
                onNext();
                break;
            // cleanup
            default:
                cleanup();
                process.exit(Boolean(error));
            }
        // guarantee cleanup on misc thrown-error
        } catch (errorCaught) {
            onNext(errorCaught);
        }
    };
    modeNext = 0;
    onNext();
}());
```

kai zhu
kaizhu256 at gmail.com



> On 20 Sep 2018, at 11:48 PM, Isiah Meadows <isiahmeadows at gmail.com> wrote:
> 
> Could you chime in with this here as an alternative?
> https://github.com/tc39/proposal-using-statement
> 
> -----
> 
> Isiah Meadows
> contact at isiahmeadows.com
> www.isiahmeadows.com
> On Thu, Sep 20, 2018 at 7:36 AM kdex <kdex at kdex.de> wrote:
>> 
>> So, when is a function supposed to be invoked when `defer`red in a generator?
>> 
>> On Thursday, September 20, 2018 11:21:06 AM CEST Ayush Gupta wrote:
>>> Hi,
>>> 
>>> I would like to propose a `defer` keyword(or something else  as the keyword
>>> name) which would allow   us to "defer" a function which will be executed
>>> once the current function either returns or throws.
>>> 
>>> The idea for this is taken from the Go programming language.
>>> 
>>> It would allow us to perform cleanup activities in a function which has
>>> multiple branches in a single place.
>>> 
>>> For example, a sample server side code can look  like:
>>> 
>>> ```  js
>>> function doSomeDbWork() {
>>>    const connection = databasepool.getConnection();
>>>    defer function () { connection.release(); } // function would be called
>>> no matter when/if the function returns or throws
>>>    //   do your work
>>> }
>>> ```
>>> 
>>> Regards
>>> Ayush Gupta_______________________________________________
>> 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/20180924/4f6bd1a3/attachment-0001.html>
-------------- next part --------------
A non-text attachment was scrubbed...
Name: Screen Shot 2018-09-24 at 1.31.45 PM copy.jpg
Type: image/jpeg
Size: 53610 bytes
Desc: not available
URL: <http://mail.mozilla.org/pipermail/es-discuss/attachments/20180924/4f6bd1a3/attachment-0001.jpg>


More information about the es-discuss mailing list