# Why doesnt this batchplay call work?

**URL:** https://forums.creativeclouddeveloper.com/t/why-doesnt-this-batchplay-call-work/5924
**Category:** UXP Plugin API
**Tags:** uxp, javascript
**Created:** [February 25, 2023, 7:25pm UTC](https://forums.creativeclouddeveloper.com/t/why-doesnt-this-batchplay-call-work/5924 "2023-02-25T19:25:40Z")
**Posts on this page:** 5
**Page:** 1

<div class="post-metadata">

### Author: ![Zob](https://avatars.discourse-cdn.com/v4/letter/z/87869e/32.png) [@Zob](https://forums.creativeclouddeveloper.com/u/Zob)
#### Post date: [February 25, 2023, 7:25pm UTC](https://forums.creativeclouddeveloper.com/t/why-doesnt-this-batchplay-call-work/5924/1 "2023-02-25T19:25:41Z")

</div>

I am trying to learn batchplay by building single step functions, such as duplicate layer, or create a new fill layer. But one of them doesn’t work and I can’t figure out why.

I have gotten duplicate layer to work after [grokking some out of date documentation,](http://forums.creativeclouddeveloper.com/t/how-to-get-batchplay-to-work-with-api-2/3760/38) but Create Fill Layer still fails with no error or debug messages. In addition, there appears to be a spelling error in the command, which makes me even more lost.

Process:

1. use Plugin\> Development \> Record Action Command
2. press the new adjustment layer button in the Layers panel.
3. select new fill layer.
4. pick a color.
5. layer is created, I end the Action Recording.

I then plug this into a function, the same way I did with duplicate layer.  
**Please make note that the “green” variable was spelled “grain” by Photoshop itself. I have tried both green and grain**

```auto
function createFillLayer(r=255.0, g=255.0, b=255.0){
    async function _createFillLayer(r, g, b){
        result = await batchPlay(
            [
                {"_obj":"make","_target":[{"_ref":"contentLayer"}],"using":{"_obj":"contentLayer","type":{"_obj":"solidColorLayer","color":{"_obj":"RGBColor","blue":b,"grain":g,"red":r}}}}
                ], {}
        )
        //console.warn(result)
    }
    utils.executeAsModalHelper(_createFillLayer(r, g, b), "fill layer")
    return
}

```

in Utils.js:

```auto
async function executeAsModalHelper (
  targetFunction,
  commandName="myCommand",
  suspendHistory = true
) {
  /*handles errors in execute as modal for you.

    targetFunction : the func to excecute. It must have the following signature:
                    async function targetFunction(executionContext, descriptor)

    options : 1. commandName (optional): A string describing the command.     
                This string is shown in the progress bar UI.

                2. suspendHistory (optional): Should the entire targetFunction be 1 history call? This needs to be re

                3. interactive (optional): A boolean to toggle interactive mode.
    */
  try {
    await executeAsModal(targetFunction, { commandName: commandName })
  } catch (e) {
    if (e.number == 9) {
      showAlert(
        'executeAsModal was rejected (some other plugin is currently inside the modal scope'
      )
      console.error(
        'executeAsModal was rejected (some other plugin is currently inside the modal scope'
      )
    } else {
      // This case is hit if the targetFunction throws an exception
      console.error(str(targetFunction) + ' function failed!')
    }
  }
}

```

However, this function works fine, and I cant figure what the difference is.

```auto
function duplicateLayer(layerToCopy="Color Fill 2", layerIDToCopy=1079, newLayerID=1080){
    async function _duplicateLayer(layerToCopy="Color Fill 2", layerIDToCopy=1079, newLayerID=1080){
    result = await batchPlay(
        [
            {"_obj":"select","_target":[{"_name":"Color Fill 2","_ref":"layer"}],"layerID":[1079],"makeVisible":false},
            {"ID":[1080],"_obj":"duplicate","_target":[{"_enum":"ordinal","_ref":"layer"}],"version":5}
            ], {}
                )
    }

    utils.executeAsModalHelper(_duplicateLayer, "DuplicateLayer")

}

```

---

<div class="post-metadata">

### Author: ![Karmalakas](https://sea1.discourse-cdn.com/flex015/user_avatar/forums.creativeclouddeveloper.com/karmalakas/32/1048_2.png) [@Karmalakas](https://forums.creativeclouddeveloper.com/u/Karmalakas)
#### Post date: [February 25, 2023, 10:04pm UTC](https://forums.creativeclouddeveloper.com/t/why-doesnt-this-batchplay-call-work/5924/2 "2023-02-25T22:04:48Z")

</div>

What result do you get from BP?

P. S. `grain` instead of `green` is not an error, but some legacy from old days. There’s info on forum.

---

<div class="post-metadata">

### Author: ![Jarda](https://sea1.discourse-cdn.com/flex015/user_avatar/forums.creativeclouddeveloper.com/jarda/32/1909_2.png) [@Jarda](https://forums.creativeclouddeveloper.com/u/Jarda)
#### Post date: [February 26, 2023, 11:27am UTC](https://forums.creativeclouddeveloper.com/t/why-doesnt-this-batchplay-call-work/5924/3 "2023-02-26T11:27:11Z")

</div>

> [@Zob](#):
>
> ```auto
> async function _createFillLayer(r, g, b){
> result = await batchPlay(
> [
> {"_obj":"make","_target":[{"_ref":"contentLayer"}],"using":{"_obj":"contentLayer","type":{"_obj":"solidColorLayer","color":{"_obj":"RGBColor","blue":b,"grain":g,"red":r}}}}
> ], {}
> )
> //console.warn(result)
> }
> 
> ```

I think this returns `Promise<void>`. Instead, you should return the function to execute.

---

<div class="post-metadata">

### Author: ![Karmalakas](https://sea1.discourse-cdn.com/flex015/user_avatar/forums.creativeclouddeveloper.com/karmalakas/32/1048_2.png) [@Karmalakas](https://forums.creativeclouddeveloper.com/u/Karmalakas)
#### Post date: [February 26, 2023, 12:08pm UTC](https://forums.creativeclouddeveloper.com/t/why-doesnt-this-batchplay-call-work/5924/4 "2023-02-26T12:08:23Z")

</div>

BTW, in your code you never show what `_createFillLayer(r, g, b)` returns. If it’s not a function, it just can’t work. You should change to

```auto
utils.executeAsModalHelper(() => _createFillLayer(r, g, b), "fill layer")

```

---

<div class="post-metadata">

### Author: ![Zob](https://avatars.discourse-cdn.com/v4/letter/z/87869e/32.png) [@Zob](https://forums.creativeclouddeveloper.com/u/Zob)
#### Post date: [February 27, 2023, 9:21pm UTC](https://forums.creativeclouddeveloper.com/t/why-doesnt-this-batchplay-call-work/5924/5 "2023-02-27T21:21:04Z")

</div>

Thanks for the responses and ideas, you guys!  
I got this to work. I think it was partially that I was passing in \_createFillLayer() using parenthesis, and so it was calling \_createFillLayer (which has no return value) when createFillLayer was defined, turning into type “null” instead of a function reference.

So first, I stepped back and redid my executeAsModalHelper to take an array of args and then unpack them using the spread operator “…”. I unpack them in an arrow function so that it doesn’t fire the function during startup. This works pretty well, including for a few other actions I’ve recorded. Here is the new version:

```auto

async function executeAsModalHelper (
  targetFunction,
  targetFunctionArgs=[],
  commandName="myCommand"
) {

  try {
    await executeAsModal(()=>{targetFunction(...targetFunctionArgs)}, { commandName: commandName })
  } catch (e) {
    if (e.number == 9) {
      showAlert(
        'executeAsModal was rejected (some other plugin is currently inside the modal scope'
      )
      console.error(
        'executeAsModal was rejected (some other plugin is currently inside the modal scope'
      )
    } else {
      // This case is hit if the targetFunction throws an exception
      console.error(str(targetFunction) + ' function failed!')
      console.warn(e)
    }
  }
}

```

And now functions which use executeAsModal that are called via buttons follow this template:

```auto
function buttonCallback(){
  async function _buttonCallback(arg1, arg2, arg3="blah"){
    await batchPlay( 
    //action Descriptor where arg1, arg2, etc are inserted into. 
    //Dont forget the {} as the second arg of batchPlay!!
    )
  }
  let args = []
  //Code to get args from current document, such as foreground color, active layer, whatever
  executeAsModalHelper(_buttonCallback, args)
}

```

You can also probably pass arguments into the buttonCallback using either .bind() or an arrow function, in case you want some hard-coded inputs per-button. But once you pass at least 1 argument, the first argument slot is filled with I think an event object that is passed from the button itself, to give info about the bubbling phase and other stuff I think? So yeah, first arg slot is auto-filled by that!

Not sure if this is a smart way to do things, but it at least has some organization and batchplay now works!
