Photoshop.app Properties Returning as null Proxies

So, what’s going on here?

I can’t get direct access to anything from photoshop.app - documents, activeDocument, layers, etc, but I can iterate over them?
I’ve created a new starter plugin and tested the same and it works as expected, so what might be causing this to happen in my plugin?

You’re not the only one.

No comments from Adobe side
Pinging @kerrishotts as it’s becoming an issue for more and more people already

@Karmalakas yeah, I saw your thread, I debated commenting there or here and figured more threads = more exposure.

I’ve tried commenting out every line of code in my index.js so that all it does is require photoshop and console.log the active document and I get the same result, so I don’t see how it’s me that has inadvertently caused it.

The type of documents and layers (and the like) is different from apiLevel 1 and 2.

  • apiLevel 1: app.documents => Array
  • apiLevel 2: app.documents => Proxy

Things like layers and documents are not Arrays in API Level 2 – they are iterable proxies that expose similar methods, but console.log is clueing you in to the fact that these are not derived from Array.

This is for performance reasons (especially due to the underlying need to use batchPlay): Ps doesn’t need to construct an object for a given index until you ask for it. This lets you avoid the cost of creating objects for the first 10 items when you ask for the eleventh, for example.

For cases where you want to treat these properties like an array, you can use Array.from or spread to convert the array-like to an Array:

console.log(app.documents); // Proxy {}
const documentsArray = Array.from(app.documents); // also [...app.documents] would work
console.log(documentsArray); // (2) [Document, Document]

Note that when the console logs out the Proxy, it’s not listing out the elements, but you shouldn’t take that as the property being empty.

3 Likes

This should be in the docs

Thanks for the clear and concise explanation @kerrishotts, it now makes sense why they returned as arrays when I made a new starter template project to test.

I agree with @Karmalakas that the docs should be updated to reflect this as it’s such a fundamental part of the API.

More broadly speaking, it strikes me that an API should be abstracting this kind of stuff away from the user - I’m struggling to think of how this benefits us over having an array returned as per API 1, bar your example. But I am by no means knowledgeable enough to to really argue this point.

As Kerri explained, performance. I assume it’s similar to PHP Generators, where you don’t have any values until you really need them and try to access. That’s pretty much how I imagined Proxies are working, just a bit frustrating to always keep guessing and not a word in the docs :frowning:

I think that proxy is the way to go. You could modify prototype of array and add something custom but it would change all array everywhere and that is dirty and you shouldn’t do that.

But with proxy you can have a typical array but we custom extra features. E.g array(proxy) of layers with custom method e.g. “sortByLayerIndex()”

Otherwise, you would need to place that method elsewhere. So you would have e.g. instance of LayersCollection class and inside would be array property layers

So you would call it e.g. collection.sortByLayerIndex() and collection.layers[0].name instead of collection[0].name. It would be unnecessary nested and it would make a coding a bit less elegant.

1 Like

:laughing:

Thanks, @Jarda, I’ve been swotting up and what you say makes a lot of sense.

Time to go learn how to write prototypes better!