13

I'm trying to call super method save() from child instance.

// ImageKeeper.js
'use strict';

module.exports = class ImageKeeper extends FileKeeper {
  constructor(keeperPath, options) {
    super(`/${keeperPath}`, options)
    this.keeperPath = keeperPath;
  }

  save(filePath) {
    return new Promise((resolve, reject) => {
      this
        .resolvePath(filePath)
        .then(function(fileData) {
          var filePath = fileData[0],
            mime = fileData[1];

          super.save(filePath, mime); // <-- GETTING ERROR HERE
        })
        .catch(function(err) {
          reject(err)
        })
    })
  }
}

// FileKeeper.js
'use strict';

module.exports = class FileKeeper {
  constructor(keeperPath, options) {
    this.storagePath = path.resolve(`${env.paths.storage}${keeperPath}`);
    this.options = options;
  }

  save(filePath, mime) {
    return Promise
      ...
  }
}

I'm getting error:

/src/filekeeper/imagekeeper.js:110
          super.save(filePath, mime);
          ^^^^^

SyntaxError: 'super' keyword unexpected here

If I move super.save(filePath, mime); in the beginning of save() method, it works.

I've tried bind context to upper scope:

save(filePath) {
  return new Promise((resolve, reject) => {
    this
      .then((fileData) => { // <-- bind context to upper scope

        super.save(filePath, mime);

But I'm getting:

Unhandled rejection SyntaxError: 'super' keyword unexpected here
    at processImmediate [as _immediateCallback] (timers.js:374:17)
From previous event:
    at /src/filekeeper/imagekeeper.js:106:10

Read this, but no luck.

Any ideas? Thank you.

env

root@8d1024b233c3:/src# node -v
  v4.1.1

docker -v
  Docker version 1.8.2, build 0a8c2e3
f1nn
  • 6,989
  • 24
  • 69
  • 92

1 Answers1

10

Looks like you've found a bug in V8's handling of super; I've reported the bug here and they've triaged it as Type-Bug and Priority-Medium. This is after looking into it in some detail, resulting in my posting this question, where this answer confirmed my suspicion it was a V8 bug.

Provided you use arrow functions (not function functions) as per your "I've tried bind context to upper scope" comment (the main code block is using a function function, which won't work), it should be working.

While waiting for a fix, it works if you put that logic in a method:

someAppropriateName(fileData) {
  var filePath = fileData[0],
    mime = fileData[1];

  super.save(filePath, mime);
}

...and call that method from the promise callback:

save(filePath) {
  return new Promise((resolve, reject) => {
    this
      .resolvePath(filePath)
      .then(fileData => {                      // **
          this.someAppropriateName(fileData);  // **
      })                                       // **
      .catch(function(err) {
        reject(err)
      })
  })
}

or:

save(filePath) {
  return new Promise((resolve, reject) => {
    this
      .resolvePath(filePath)
      .then(this.someAppropriateName.bind(this)) // **
      .catch(function(err) {
        reject(err)
      })
  })
}

That works because the bug is fairly obscure: It only crops up if you have an arrow function inside another arrow function inside a method, and the innermost arrow function uses a variable or argument defined by the outer arrow function (using stuff from the method itself is fine).


Some other notes, though:

  1. If FileKeeper's save returns a promise, it seems like ImageKeeper should be using that and chaining off it. Your code just throws away the result of calling super.save(...).

  2. When you find yourself writing new Promise, always stop and ask yourself if the code in question is really the root of the chain. Very, very, very often it isn't (and I suspect it isn't in your code). Remember that every then returns a promise, and the power of promises lies primarily in the chain. Don't break the chain if you don't have to.

Community
  • 1
  • 1
T.J. Crowder
  • 1,031,962
  • 187
  • 1,923
  • 1,875
  • Thank you for deep investigation. Right now I've ended with moving inner function into separate one and applying context with `this._save.bind(this)`. Also, removed extra Promise constructors, thank you and @Benjamin for taking time to point me out this flaw :). – f1nn Oct 06 '15 at 08:24
  • @f1nn: No worries. It was very interesting, and answering the question proved to me that my understanding of `super` was, um, superficial. :-) Learned a lot in the process of fixing that. – T.J. Crowder Oct 06 '15 at 08:29