1

There's a github package called pngjs-draw that draws primitives onto a png. Just what I need, but I'd like it to run alongside other async code that uses promises. I'm attempting to do that, running into something I don't understand about "this". The pngjs-draw example code from github looks like this...

var fs = require('fs');
var drawing = require('pngjs-draw');
var png = drawing(require('pngjs').PNG);

fs.createReadStream("blue.png")
  .pipe(new png({ filterType: 4 }))
  .on('parsed', function() {
    // Draws a pixel with transparent green
    this.drawPixel(150,200, this.colors.black())
    // more examples snipped
    // Writes file
    this.pack().pipe(fs.createWriteStream('blue.out.png'));
  });

It uses "this" inside the 'parsed' function, and this seems to be an instance of something that has all of the drawing methods.

I'd like to make a more generic version, except return a promise, handle errors, and so on. But it seems I can't use "this" in the same way when creating the promise. Here's my attempt...

function drawOnPNG(fileIn, fileOut, drawFunction) {
  return new Promise((resolve, reject) => {
      fs.createReadStream(fileIn).pipe(new png({ filterType: 4 })).on('parsed', () => {
          drawFunction(this);  // <--MY PROBLEM, I THINK
          this.pack().pipe(fs.createWriteStream(fileOut)).on('finish', () => resolve()).on('error', error => reject(error));
      }).on('error', error => reject(error));        
  });
}

I thought it would be handy to make drawing functions like this...

function someDrawFunction(pngThing) {
    pngThing.drawPixel(150,200, this.colors.black());
}

Then I could draw like this...

drawOnPNG('fileIn.png', 'fileOut.png', someDrawFunction).then(...

But, when this executes, pngThing isn't what I hoped. Inspecting it with the debugger, it's a very big object with seemingly all the JS classes defined on it. Can you suggest a way to access the drawing object inside the promise creation?

user1272965
  • 2,284
  • 6
  • 27
  • 44
  • 1
    This has nothing to do with the promise constructor, and everything to do with the change from `.on('parsed', function() {` to `.on('parsed', () => {` – Bergi Jun 15 '18 at 16:01
  • Thanks @Bergi. The duplicate helped me understand that I misdiagnosed the error to be about promises, rather than arrow functions. Is the SO rule that different questions which have the *same answer* are dups? Seems like there's a some stigma attached to having one's question marked as dup (not doing one's homework, and so on), but I don't think I would have found that dup through research, given my basic misunderstanding. Anyway, very glad to have gotten the help figuring it out. Thanks again. – user1272965 Jun 15 '18 at 16:21
  • 1
    There is no intentional stigma around a question being closed. Being closed as a dupe is just used to quickly link you and anyone else who might come across this to an answer. Being closed as a dupe doesn't indicate that the other question is better, just that your answer is already there; sometimes the question that gets closed is better written, but the other one already has answers. In this case the premise was based on a misunderstanding of where the error came from, so just closing as a dupe would probably not be enough, but closing and leaving a comment as Bergi did is sufficient. – Paul Jun 15 '18 at 19:36

2 Answers2

2

.on() is calling its callback with some specific this (probably the png object).

Using an arrow function inherits this from its lexical scope, not the callsite, so you ignore that this.

You should use a normal function() {} expression so you pick up the this passed from the callsite.

SLaks
  • 800,742
  • 167
  • 1,811
  • 1,896
1

Your problem has nothing to do with switching to use a Promise. It's that you switched from a regular function to an arrow function for the callback to the 'parsed' event. Arrow functions do not have their own this. The fix is to switch () => back to function () { and add the corresponding } back in.

Paul
  • 130,653
  • 24
  • 259
  • 248