@michael, integration-level javascript rarely deals with blocking-code. its
mostly tedious-work debugging and “fixing” endless piles of async-io timeout
issues, without the help of a stack-trace :`( and the term “fixing” is
oftentimes euphemism for “rewriting the carefully architected backend into
something simpler-and-dumber, because it turns out to have too many timeout
issues during integration”.
you can’t seriously call yourself a senior-developer if you insist on using
irrelevant blocking-code design-patterns like function-composition or
pipeline-operators at such a level, which mostly gets in the way of your
debugging and “fixing" tasks. and using these features at the lower
library-level only reinforce blocking-code-mindset bad-habits that will set you
up for failure, when you're promoted to take on integration-level
responsibilities.
btw, 2 of the async-steps in the example provided requires listening to
triggered-events to proceed. subjectively, the stack-overflow workaround i
found for async/await to handle events [1] looks a bit more hacky and less
clean than my example:
```
async function fn() {
await a();
await b();
const [{data}, cResult] = await Promise.all([
new Promise(resolve => thing.once('myEvent', resolve)),
c()
]);
return data;
}
```
but if you think you can create cleaner-code than what i posted using
async/await, then feel free to try.
-kai
[1]
https://stackoverflow.com/questions/43084557/using-promises-to-await-triggered-events
<https://stackoverflow.com/questions/43084557/using-promises-to-await-triggered-events>
> On Mar 16, 2018, at 8:35 AM, Michael J. Ryan <[email protected]> wrote:
>
> Does the existence of a pipeline operator stop such code from working?
>
> Frankly, I'd probably structure your example more as an async function...
>
> --
> Michael J. Ryan - http://tracker1.info <http://tracker1.info/>
>
> On Thu, Mar 15, 2018 at 9:22 AM, kai zhu <[email protected]
> <mailto:[email protected]>> wrote:
>
>> On Mar 15, 2018, at 10:49 PM, Michael J. Ryan <[email protected]
>> <mailto:[email protected]>> wrote:
>>
>> And only 50x the amount of code too.
>>
>
> fair enough. but lets move from small-picture toy-cases to bigger-picture
> integration-level ones, where non-blocking code is common.
>
> here's a working, simple but useful 40-sloc real-world electron-script [1]
> which employs a single recursive-callback (function onNext()) to step-by-step
> screen-capture websites/demos to png. how would you re-express the
> linear-steps in the example into something significantly more readable with
> function-composition or pipeline-operators?
>
> ```
> /*
> * screen-capture.js
> *
> * this electron-script will screen-capture the website with the given url in
> the commandline
> *
> * exsmple usage:
> * $ electron screen-capture.js https://www.pinterest.com/
> <https://www.pinterest.com/>
> *
> * output:
> * case 1: wait for electron to init
> * case 2: open url https://www.pinterest.com/ <https://www.pinterest.com/>
> * case 3: wait 5000ms for webpage to render
> * case 4: screenshot webpage
> * case 5: save file electron.screenshot.png
> * case 6: exit electron
> */
>
> /*jslint
> bitwise: true,
> browser: true,
> maxerr: 8,
> maxlen: 96,
> node: true,
> nomen: true,
> regexp: true,
> stupid: true
> */
> (function () {
> 'use strict';
> var options, modeNext, onNext;
> modeNext = 0;
> onNext = function (data) {
> modeNext += 1;
> switch (modeNext) {
> case 1:
> console.log('case ' + modeNext + ': wait for electron to init');
> // wait for electron to init
> require('electron').app.once('ready', onNext);
> break;
> case 2:
> console.log('case ' + modeNext + ': open url ' + process.argv[2]);
> // init options
> options = { frame: false, height: 768, width: 1024, x: 0, y: 0 };
> // init browserWindow;
> options.BrowserWindow = require('electron').BrowserWindow;
> options.browserWindow = new options.BrowserWindow(options);
> // goto next step when webpage is loaded
> options.browserWindow.webContents.once('did-stop-loading',
> onNext);
> // open url
> options.browserWindow.loadURL(process.argv[2]);
> break;
> case 3:
> console.log('case ' + modeNext + ': wait 5000ms for webpage to
> render’);
> // wait 5000ms for webpage to render
> setTimeout(onNext, 5000);
> break;
> case 4:
> console.log('case ' + modeNext + ': screenshot webpage');
> // screenshot webpage
> options.browserWindow.capturePage(options, onNext);
> break;
> case 5:
> console.log('case ' + modeNext + ': save file
> electron.screenshot.png');
> // save screenshot
> require('fs').writeFile('electron.screenshot.png', data.toPng(),
> onNext);
> break;
> case 6:
> console.log('case ' + modeNext + ': exit electron');
> // exit
> process.exit(0);
> break;
> }
> };
> onNext();
> }());
> ```
>
> [1]
> https://github.com/kaizhu256/node-electron-lite#quickstart-screenshot-example
> <https://github.com/kaizhu256/node-electron-lite#quickstart-screenshot-example>
>
> <Screen-Shot-2018-03-15-at-11.59.33-PM-compressor.png>
> <Screen-Shot-2018-03-15-at-11.53.38-PM-compressor.png>
>
>> On Mar 14, 2018 16:32, "kai zhu" <[email protected]
>> <mailto:[email protected]>> wrote:
>>> On Mar 13, 2018, at 10:27 PM, Michael J. Ryan <[email protected]
>>> <mailto:[email protected]>> wrote:
>>>
>>> I'm jumping in late here, but being in that role, I can tell what it's
>>> doing mostly by looking at it... It looks like and is a pipeline. I
>>> actually find that less confusing than deeply nested calls...
>>>
>>> a(b(c(d(e), f)))
>>>
>>> which I've seen in real code... vs
>>>
>>> e |> d |> (r => c(r,f)) |> b |> a
>>>
>>> Which is a bit easier to reason with even if a little more verbose. Each
>>> step is in order instead of nesting which is reverse order.
>>
>>
>>
>> @michael, i would argue a simple, magic-free, es5 recursive-callback of the
>> following form is the easiest to read / debug / set-breakpoints-with:
>>
>> ```js
>> var callbackState, recursiveCallback;
>> recursiveCallback = function (error, data) {
>> // catch-all error-handler
>> if (error) {
>> ...
>> return;
>> }
>> callbackState += 1;
>> console.error('recursive-callback at case ' + callbackState);
>> switch (callbackState) {
>> case 1:
>> dd(ee, recursiveCallback);
>> break;
>> // combine data with ff
>> case 2:
>> cc(data, ff, recursiveCallback);
>> break;
>> case 3:
>> bb(data, recursiveCallback);
>> break;
>> case 4:
>> aa(data, recursiveCallback);
>> break;
>> }
>> };
>> callbackState = 0;
>> recursiveCallback();
>> ```
>>
>> here's the full, standalone, working example for your use-case (that’s both
>> browser and nodejs compatible) with attached screenshot of it running in
>> browser (and another one showing how easy it is to step-through and debug
>> with only a single breakpoint).
>>
>> ```
>> /*jslint
>> bitwise: true,
>> browser: true,
>> maxerr: 8,
>> maxlen: 256,
>> node: true,
>> nomen: true,
>> regexp: true,
>> stupid: true
>> */
>> 'use strict’;
>> var aa, bb, cc, dd, ee, ff, callbackState, recursiveCallback;
>> ff = 'goodbye world!';
>> ee = 'hello world!';
>> dd = function (data, recursiveCallback) {
>> console.log('dd(ee) - simulating 300ms io-request with data=' +
>> JSON.stringify(data) + ' ...');
>> setTimeout(recursiveCallback, 300, null, data);
>> };
>> cc = function (data, data2, recursiveCallback) {
>> console.log('cc(dd(ee), ff) - simulating 200ms io-request with data=' +
>> JSON.stringify(data) + ' and data2=' + JSON.stringify(data2) + ' ...');
>> setTimeout(recursiveCallback, 200, null, data + ' ' + data2);
>> };
>> bb = function (data, recursiveCallback) {
>> console.log('bb(cc(dd(ee), ff)) - simulating 100ms io-request with
>> data=' + JSON.stringify(data) + ' ...');
>> setTimeout(recursiveCallback, 100, null, data);
>> };
>> aa = function (data, recursiveCallback) {
>> console.log('aa(bb(cc(dd(ee), ff))) - printing data ' +
>> JSON.stringify(data));
>> // simulate error
>> recursiveCallback(new Error('this is a test error'));
>> };
>>
>>
>>
>> recursiveCallback = function (error, data) {
>> // catch-all error-handler
>> if (error) {
>> console.error('error occured in recursive-callback at case ' +
>> callbackState);
>> console.error(error);
>> return;
>> }
>> callbackState += 1;
>> console.error('recursive-callback at case ' + callbackState);
>> switch (callbackState) {
>> case 1:
>> dd(ee, recursiveCallback);
>> break;
>> // combine data with ff
>> case 2:
>> cc(data, ff, recursiveCallback);
>> break;
>> case 3:
>> bb(data, recursiveCallback);
>> break;
>> case 4:
>> aa(data, recursiveCallback);
>> break;
>> }
>> };
>> callbackState = 0;
>> recursiveCallback();
>>
>> /*
>> output:
>>
>> recursive-callback at case 1
>> dd(ee) - simulating 300ms io-request with data="hello world!" ...
>> recursive-callback at case 2
>> cc(dd(ee), ff) - simulating 200ms io-request with data="hello world!" and
>> data2="goodbye world!" ...
>> recursive-callback at case 3
>> bb(cc(dd(ee), ff)) - simulating 100ms io-request with data="hello world!
>> goodbye world!" ...
>> recursive-callback at case 4
>> aa(bb(cc(dd(ee), ff))) - printing data "hello world! goodbye world!"
>> error occured in recursive-callback at case 4
>> Error: this is a test error
>> at aa (/private/tmp/example.js:30:23)
>> at Timeout.recursiveCallback [as _onTimeout]
>> (/private/tmp/example.js:56:9)
>> at ontimeout (timers.js:393:18)
>> at tryOnTimeout (timers.js:250:5)
>> at Timer.listOnTimeout (timers.js:214:5)
>> */
>> ```
>>
>> <Screen-Shot-2018-03-15-at-6.35.47-AM-compressor.png>
>> <Screen-Shot-2018-03-15-at-7.21.03-AM-compressor.png>
>>
>>> On Mar 14, 2018, at 7:19 AM, Alexander Jones <[email protected]
>>> <mailto:[email protected]>> wrote:
>>>
>>> Straw man. The problem is variables named h, f and g, not the use of a
>>> composition operator.
>>>
>>> On Sun, 11 Mar 2018 at 07:37, kai zhu <[email protected]
>>> <mailto:[email protected]>> wrote:
>>> @peter, put yourself in the shoes of a senior-programmer responsible
>>> for overseeing an entire web-project. the project is @ the
>>> integration-stage and you're busy debugging an async
>>> timeout/near-timeout bug preventing the frontend from talking to the
>>> backend (which btw, is one of the most common integration/qa
>>> javascript-bugs).
>>>
>>> while trying to figure out what's causing the timeout-issue, you're
>>> debugging i/o code with operators that look like this:
>>>
>>> ```
>>> const h = ? |> f |> g;
>>> ```
>>>
>>> maybe it is useful for the small-picture sub-problem you were
>>> originally trying to solve. but now that you're a bigger-fish with
>>> bigger-picture integration i/o issues, doesn't this look alot like
>>> technical-debt that no one will have a clue how to debug once a month
>>> or two has passed?
>>>
>>> -kai
>>>
>>> On 3/11/18, Peter Jaszkowiak <[email protected]
>>> <mailto:[email protected]>> wrote:
>>> > Oh please,
>>> >
>>> > This is an alternative syntax that's very useful for many people. If you
>>> > want too simplify syntax yourself you can use a linter to disable
>>> > alternatives.
>>> >
>>> > On Mar 10, 2018 22:56, "kai zhu" <[email protected]
>>> > <mailto:[email protected]>> wrote:
>>> >
>>> >> my vote is for neither. exactly what industry painpoint or
>>> >> problem-space do either of these proposals solve?
>>> >>
>>> >> rather, they compound an existing industry painpoint; where
>>> >> ocd-programmers have problems in deciding-and-choosing which es6
>>> >> style/design-pattern to employ and stick with before coding even
>>> >> begins. many of us wish there were less choices, like python (and a
>>> >> more assertive tc39 that makes clear certain proposals are
>>> >> productivity-negative and not open for debate) so we could get on with
>>> >> the actual coding-part.
>>> >>
>>> >> from a senior-engineer / technical-manager perspective, it also
>>> >> doesn't help in managing an entire web-project; comprised of dozens of
>>> >> sub-components that you didn't all write yourself; and having to
>>> >> context-switch for each sub-component's quirky es6/es7/es8/es9
>>> >> style-guide/design-pattern.
>>> >>
>>> >> On 3/4/18, Isiah Meadows <[email protected]
>>> >> <mailto:[email protected]>> wrote:
>>> >> > Just thought I'd point out that the proposal itself entertains the
>>> >> > possibility of a corresponding composition proposal [1]. Also, in my
>>> >> > proposal, one of my "potential expansions" [2] would open a generic
>>> >> > door for "lifting" over a type, addressing the concern of
>>> >> > extensibility. (It's not ideal, and I just filed an issue in my repo
>>> >> > for that, but that's orthogonal.)
>>> >> >
>>> >> > [1]: https://github.com/tc39/proposal-pipeline-operator#
>>> >> > <https://github.com/tc39/proposal-pipeline-operator#>
>>> >> related-proposals
>>> >> > [2]:
>>> >> > https://github.com/isiahmeadows/function-composition-proposal#possible-
>>> >> >
>>> >> > <https://github.com/isiahmeadows/function-composition-proposal#possible->
>>> >> expansions
>>> >> >
>>> >> > -----
>>> >> >
>>> >> > Isiah Meadows
>>> >> > [email protected] <mailto:[email protected]>
>>> >> >
>>> >> > Looking for web consulting? Or a new website?
>>> >> > Send me an email and we can get started.
>>> >> > www.isiahmeadows.com <http://www.isiahmeadows.com/>
>>> >> >
>>> >> >
>>> >> > On Sat, Feb 24, 2018 at 5:40 AM, Naveen Chawla <[email protected]
>>> >> > <mailto:[email protected]>>
>>> >> > wrote:
>>> >> >> Although it doesn't allow composition with generator functions like
>>> >> >> the
>>> >> >> composition proposal does, otherwise it's a pretty good solution.
>>> >> >>
>>> >> >> My only concern with pipeline is that since it offers a different way
>>> >> >> of
>>> >> >> calling functions than the `()` syntax, it can lead to mixed and hence
>>> >> >> slightly more confusing code when both `()` and `|>` are used. For
>>> >> example
>>> >> >> multi arg and no-arg functions would still use `()`, and single arg
>>> >> >> functions may or may not use `|>` depending on whether or not they may
>>> >> >> prospectively use a pipeline. The composition operator doesn't
>>> >> >> supersede
>>> >> >> the
>>> >> >> `()` syntax in any context, and so it could be argued it would lead to
>>> >> >> more
>>> >> >> consistent, more readable code.
>>> >> >>
>>> >> >> On Sat, 24 Feb 2018 at 15:02 Peter Jaszkowiak <[email protected]
>>> >> >> <mailto:[email protected]>>
>>> >> wrote:
>>> >> >>>
>>> >> >>> I'd like to point out the partial application operator:
>>> >> >>> https://github.com/tc39/proposal-partial-application
>>> >> >>> <https://github.com/tc39/proposal-partial-application>
>>> >> >>>
>>> >> >>> Sounds like the combination of pipeline + partial application would
>>> >> >>> result
>>> >> >>> in what is essentially the same as function composition operator:
>>> >> >>>
>>> >> >>> ```
>>> >> >>> const h = ? |> f |> g;
>>> >> >>> ```
>>> >> >>>
>>> >> >>> Which results in `h` being the composition `g • f`.
>>> >> >>>
>>> >> >>>
>>> >> >>> On Feb 24, 2018 02:21, "Naveen Chawla" <[email protected]
>>> >> >>> <mailto:[email protected]>> wrote:
>>> >> >>>
>>> >> >>> That could be a problem for readability.
>>> >> >>> I agree with the rest of what you said.
>>> >> >>>
>>> >> >>>
>>> >> >>> On Sat, 24 Feb 2018 at 11:16 Viktor Kronvall <
>>> >> [email protected] <mailto:[email protected]>>
>>> >> >>> wrote:
>>> >> >>>>
>>> >> >>>> I don’t know the implications but I could easily imagine the
>>> >> >>>> pipeline
>>> >> >>>> proposal being extended to not taking any input on the left hand
>>> >> >>>> side
>>> >> >>>> and
>>> >> >>>> effectively represent composition in the opposite direction.
>>> >> >>>>
>>> >> >>>> For example:
>>> >> >>>> ```
>>> >> >>>> let h = |> f |> g
>>> >> >>>> h(2) //g(f(2))
>>> >> >>>> ```
>>> >> >>>>
>>> >> >>>> That said, the point holds for the proposal in its current state.
>>> >> Being
>>> >> >>>> able to compose functions
>>> >> >>>> leads to much more expressivity than if you have
>>> >> >>>> to call the pipeline (and collapse) where it is defined.
>>> >> >>>> 2018年2月24日(土) 14:32 Naveen Chawla <[email protected]
>>> >> >>>> <mailto:[email protected]>>:
>>> >> >>>>>
>>> >> >>>>> The function composition operator composes function pipelines into
>>> >> >>>>> functions for later use and/or further composition. Those functions
>>> >> >>>>> still
>>> >> >>>>> need to be called via the existing `()` syntax, so it doesn't offer
>>> >> >>>>> a
>>> >> >>>>> different way of calling functions as such.
>>> >> >>>>>
>>> >> >>>>> The function pipeline operator calls the function pipeline
>>> >> immediately,
>>> >> >>>>> so it is really only a different way of calling functions.
>>> >> >>>>>
>>> >> >>>>> On Fri, 23 Feb 2018 at 12:37 Jordan Harband <[email protected]
>>> >> >>>>> <mailto:[email protected]>>
>>> >> wrote:
>>> >> >>>>>>
>>> >> >>>>>> How is either operator not "a different way of calling functions"?
>>> >> >>>>>>
>>> >> >>>>>> On Thu, Feb 22, 2018 at 8:34 PM, Naveen Chawla <
>>> >> [email protected] <mailto:[email protected]>>
>>> >> >>>>>> wrote:
>>> >> >>>>>>>
>>> >> >>>>>>> I was just thinking about the relative merits and coexistence (or
>>> >> >>>>>>> not)
>>> >> >>>>>>> of function composition operator and function pipeline operator
>>> >> >>>>>>> features:
>>> >> >>>>>>>
>>> >> >>>>>>> e.g.
>>> >> >>>>>>>
>>> >> >>>>>>> https://github.com/TheNavigateur/proposal-pipeline-operator-for-
>>> >> >>>>>>> <https://github.com/TheNavigateur/proposal-pipeline-operator-for->
>>> >> function-composition
>>> >> >>>>>>> https://github.com/tc39/proposal-pipeline-operator
>>> >> >>>>>>> <https://github.com/tc39/proposal-pipeline-operator>
>>> >> >>>>>>>
>>> >> >>>>>>> They can of course co-exist, but there is overlap only in the
>>> >> respect
>>> >> >>>>>>> that both allow function pipelines to be called from left to
>>> >> >>>>>>> right
>>> >> >>>>>>> (except
>>> >> >>>>>>> the input parameter in the case of the composition feature, which
>>> >> >>>>>>> requires
>>> >> >>>>>>> existing bracket syntax to be used to call it). If one were to be
>>> >> >>>>>>> chosen,
>>> >> >>>>>>> would say that a function composition operator adds a whole new
>>> >> >>>>>>> dimension of
>>> >> >>>>>>> expressive power to the language, whereas a pipeline operator
>>> >> >>>>>>> only
>>> >> >>>>>>> offers a
>>> >> >>>>>>> different way of calling functions.
>>> >> >>>>>>>
>>> >> >>>>>>> I was wondering about all of your thoughts about whether you'd
>>> >> prefer
>>> >> >>>>>>> only the pipeline operator, only the composition operator, or
>>> >> >>>>>>> both,
>>> >> >>>>>>> or
>>> >> >>>>>>> neither to be added to the language (these are pretty much all
>>> >> >>>>>>> the
>>> >> >>>>>>> possibilities), and why.
>>> >> >>>>>>>
>>> >> >>>>>>> _______________________________________________
>>> >> >>>>>>> es-discuss mailing list
>>> >> >>>>>>> [email protected] <mailto:[email protected]>
>>> >> >>>>>>> https://mail.mozilla.org/listinfo/es-discuss
>>> >> >>>>>>> <https://mail.mozilla.org/listinfo/es-discuss>
>>> >> >>>>>>>
>>> >> >>>>>>
>>> >> >>>>> _______________________________________________
>>> >> >>>>> es-discuss mailing list
>>> >> >>>>> [email protected] <mailto:[email protected]>
>>> >> >>>>> https://mail.mozilla.org/listinfo/es-discuss
>>> >> >>>>> <https://mail.mozilla.org/listinfo/es-discuss>
>>> >> >>>
>>> >> >>>
>>> >> >>> _______________________________________________
>>> >> >>> es-discuss mailing list
>>> >> >>> [email protected] <mailto:[email protected]>
>>> >> >>> https://mail.mozilla.org/listinfo/es-discuss
>>> >> >>> <https://mail.mozilla.org/listinfo/es-discuss>
>>> >> >>>
>>> >> >>>
>>> >> >>> _______________________________________________
>>> >> >>> es-discuss mailing list
>>> >> >>> [email protected] <mailto:[email protected]>
>>> >> >>> https://mail.mozilla.org/listinfo/es-discuss
>>> >> >>> <https://mail.mozilla.org/listinfo/es-discuss>
>>> >> >>
>>> >> >>
>>> >> >> _______________________________________________
>>> >> >> es-discuss mailing list
>>> >> >> [email protected] <mailto:[email protected]>
>>> >> >> https://mail.mozilla.org/listinfo/es-discuss
>>> >> >> <https://mail.mozilla.org/listinfo/es-discuss>
>>> >> >>
>>> >> > _______________________________________________
>>> >> > es-discuss mailing list
>>> >> > [email protected] <mailto:[email protected]>
>>> >> > https://mail.mozilla.org/listinfo/es-discuss
>>> >> > <https://mail.mozilla.org/listinfo/es-discuss>
>>> >> >
>>> >> _______________________________________________
>>> >> es-discuss mailing list
>>> >> [email protected] <mailto:[email protected]>
>>> >> https://mail.mozilla.org/listinfo/es-discuss
>>> >> <https://mail.mozilla.org/listinfo/es-discuss>
>>> >>
>>> >
>>> _______________________________________________
>>> es-discuss mailing list
>>> [email protected] <mailto:[email protected]>
>>> https://mail.mozilla.org/listinfo/es-discuss
>>> <https://mail.mozilla.org/listinfo/es-discuss>
>>
>
>
_______________________________________________
es-discuss mailing list
[email protected]
https://mail.mozilla.org/listinfo/es-discuss