Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[NON-BREAKING] ES2015 Proxy approach (requires Node.js 6) #32

Closed
wants to merge 2 commits into from

Conversation

schnittstabil
Copy link
Contributor

See: #29

@schnittstabil
Copy link
Contributor Author

schnittstabil commented Sep 17, 2016

argumentList can just be passed as an array.

If I understand you correctly, that would also imply:

const processFn = (fn, opts) => function (args) {
// …
}
// …
        get: (target, key) => {
            // …
            return function() {
                return cached(arguments); // or array version of arguments
            };
        }

@sindresorhus
Copy link
Owner

sindresorhus commented Sep 17, 2016

Not entirely sure what you're asking about in that example. Can you clarify?

index.js Outdated
for (let i = 1; i < arguments.length; i++) {
results[i - 1] = arguments[i];
}
const [, ...results] = arguments;
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No need to use arguments. Just use the rest operator in the function. Can switch to arrow function then too.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sadly, arrow functions don't have arguments...

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah, ok....

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Re-read my comment. Rest parameters ftw ;)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@sindresorhus Which version do you prefer:

        args.push((err, result, ...results) => {
            if (err) {
                reject(err);
            } else if (opts.multiArgs) {
                resolve([result, ...results]);
            } else {
                resolve(result);
            }
        });

or

        args.push((err, ...results) => {
            if (err) {
                reject(err);
            } else if (opts.multiArgs) {
                resolve(results);
            } else {
                resolve(results[0]);
            }
        });

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

First. We should optimize for the common case.

@schnittstabil
Copy link
Contributor Author

I don't see benefits, e.g every get returns a newly created function:

            return function() {
                return cached(arguments); // or array version of arguments
            };

@sindresorhus
Copy link
Owner

sindresorhus commented Sep 17, 2016

Easier to explain with code. This is what I was thinking: 65d5fc9

It's probably not entirely correct as some tests were failing, but I'm too sleepy to figure out why. But I hope it shows what I'm looking for.

@schnittstabil
Copy link
Contributor Author

65d5fc9: Maybe it's not covered by the tests, but I think you have to change the return value of get too!?

@sindresorhus
Copy link
Owner

@schnittstabil I wanna ship this, but can't realistically just target Node.js 6 for a good while. How about we put this in a separate file and ship it now? People targeting Node.js 6 can use it, and it wouldn't make a difference for Node.js 4 users. For example: const pify = require('pify/proxy'); (Or a better name?)

@schnittstabil
Copy link
Contributor Author

require('pify/proxy') sounds reasonable.

Honestly, I do not believe it works to all extend, but it seems ready for most use cases. Hence, I would suggest to either release it for internal usage only or document it as beta or similar.

@sindresorhus
Copy link
Owner

Could you move it to a separate file?

@schnittstabil
Copy link
Contributor Author

Sorry, not until Monday.

@schnittstabil
Copy link
Contributor Author

@sindresorhus Doesn't really LGTM, but proxy stuff moved into proxy.js.

Remaining issues I see so far:

  1. How should we deal with [[Set]]? For example:
    const fsP = pify(fs);
    fsP.stat = p => new Promise();
    // and
    fsP.stat = (p, cb) => {};
  2. How should we deal with non-writable, non-configurable properties? For example:
    const fsP = pify(fs);
    Object.freeze(fs);
    fs.stat === fsP.stat // <- MUST be true!?
    From [[Get]] (P, Receiver):

    [[Get]] for proxy objects enforces the following invariants:

    • The value reported for a property must be the same as the value of the corresponding target object property if the target object property is a non-writable, non-configurable own data property.

import fs from 'fs';
import test from 'ava';
import pinkiePromise from 'pinkie-promise';
import fn from './proxy';
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

test-proxy.js is (almost) an one-to-one duplicate of test.js 😒

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That's ok for now. We'll either replace proxy with the current approach or adopt both in the same API once we can target Node.js 6.

@thom4parisot
Copy link

thom4parisot commented Jan 24, 2017

Don't you want to have two branches (one for non-proxy code, and one for node6-proxy), and to publish to two different npm tags (master to latest, node6-proxy to node6-proxy or next)?

The breaking change would occur when you decide to move the node6-proxy to latest.

Just a thought, not even an opinion :-)

@schnittstabil
Copy link
Contributor Author

@oncletom I'm not sure if I understand you correctly. As far as I can see, merging this PR into master is not a breaking change (anymore):

  • nothing changed for node4 and node6 users if they require('pify')
  • node6 users can also require('pify/proxy'), thus proxies are only an additional functionality

@thom4parisot
Copy link

@schnittstabil yeah sorry, I was not clear at all. I rephrased some of my thoughts and I totally get the fact you ship both approaches in the same branch, but with an additional entrypoint for people having a node environment which has Proxy enabled.

@schnittstabil schnittstabil changed the title [BREAKING] ES2015 Proxy approach requires Node.js 6 [NON-BREAKING] ES2015 Proxy approach (requires Node.js 6) Jan 24, 2017
@sindresorhus
Copy link
Owner

  1. Just let them override. We shouldn't do anything. Pretty edge-casy anyways.

  2. We document it as a caveat. I really doubt anyone will hit this. I've yet to see Object.freeze() actually used. And if they try using it, it will throw:

'get' on proxy: property 'stat' is a read-only and non-configurable data property on the proxy target but the proxy did not return its actual value

import fs from 'fs';
import test from 'ava';
import pinkiePromise from 'pinkie-promise';
import fn from './proxy';
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That's ok for now. We'll either replace proxy with the current approach or adopt both in the same API once we can target Node.js 6.

proxy.js Outdated

for (let i = 0; i < arguments.length; i++) {
args[i] = arguments[i];
}
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could you also integrate 65d5fc9? Would like to remove this boilerplate code.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Unfortunately, the version below breaks optimization:

const processFn = (fn, opts) => function (...args) { 

And the following version don't work well too:

const processFn = (fn, opts) => function (args) { 

handler.get have to be able to return processFn(x, opts) with the function signature of x. We could fix the signature in handler.get, but that wouldn't be very sensible in my opinion.

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Alright, let's just hold off on changing that until Node.js 8 which should fix the perf deoptimization.

@sindresorhus
Copy link
Owner

@schnittstabil I totally forgot about this one. Anything left before we can merge? Can you fix the merge conflict?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

4 participants