# How to call a function asynchronously in Perspective session

**URL:** <https://forum.inductiveautomation.com/t/how-to-call-a-function-asynchronously-in-perspective-session/39229>\
**Category:** Ignition\
**Tags:** scripting, ignition80, perspective\
**Created:** [September 30, 2020, 3:18pm UTC](https://forum.inductiveautomation.com/t/how-to-call-a-function-asynchronously-in-perspective-session/39229 "2020-09-30T15:18:57Z")\
**Posts on this page:** 10\
**Page:** 1

<div class="post-metadata">

**Author:** ![YF129701](https://sea1.discourse-cdn.com/inductiveautomation/user_avatar/forum.inductiveautomation.com/yf129701/32/60498_2.png) [@YF129701](https://forum.inductiveautomation.com/u/YF129701)\
**Post date:** [September 30, 2020, 3:18pm UTC](https://forum.inductiveautomation.com/t/how-to-call-a-function-asynchronously-in-perspective-session/39229/1 "2020-09-30T15:18:57Z")

</div>

I have a system I created with two views. On the first view is a table, and when a row is selected and the `Edit` button is clicked, a new view pops up with the selected row data passed in as a parameter. On the second view is a form and another table. Users enter information into the form, which adds a new row to the second table. Also on the second view is a `Complete` button, which takes every single row in the second table and inserts it into a database through an API (to an onsite webserver). On a successful insert, the second view closes, and the items appear in a row subview (if expanded) of the first table.

The problem is, if the second table has more than about 5 entries in the table, the UI lags considerably. For instance, a user reported that when they had 10 entries, the UI didn’t respond for 15 seconds. This is very inconvenient.

I would like to insert the records asynchronously, so that the popup view closes and then the necessary data is inserted when available. I used the `system.util.invokeAsynchronous` and `system.util.invokeLater` functions in Vision projects before, but the scope for the `invokeLater` function is listed as Vision only. Here is the example listed in the documentation:

```auto
def longProcess(rootContainer = event.source.parent):
   import system
   results = ...( something )
   def sendBack(results = results, rootContainer = rootContainer):
      rootContainer.resultsProperty = results
   system.util.invokeLater(sendBack) # only valid for Vision scope?
    
system.util.invokeAsynchronous(longProcess) 

```

This example doesn’t work with a Perspective session. How would I call a function asynchronously in a Perspective session? Also, is a new thread created on the Ignition server for every individual session?

A few more things to note: the API is third-party, something I have no control over. There is no batch insert, so I iterate over every row in the second table manually. Also, when I use Postman or `curl` to insert, the time to insert a single record takes about ~250ms.

---

<div class="post-metadata">

**Author:** ![paul-griffith](https://sea1.discourse-cdn.com/inductiveautomation/user_avatar/forum.inductiveautomation.com/paul-griffith/32/37249_2.png) [@paul-griffith](https://forum.inductiveautomation.com/u/paul-griffith)\
**Post date:** [September 30, 2020, 3:32pm UTC](https://forum.inductiveautomation.com/t/how-to-call-a-function-asynchronously-in-perspective-session/39229/2 "2020-09-30T15:32:41Z")

</div>

Every call to `invokeAsynchronous` creates an additional thread. Be judicious about the use of the function.

There’s no equivalent to `invokeLater` in Perspective, because Perspective’s single-threaded event dispatcher is all on the frontend (the browser/device/workstation app) happening in the wide, wonderful world of Javascript. All scripting exposed to users, meanwhile, is happening on the ‘backend’, so that you can continue to use all the Ignition scripting paradigms you’re used to.

What I would recommend is using invokeAsynchronous as you are, and passing a session ID and/or page ID into the asynchronous thread. Then from the async thread, you can call [`system.perspective.sendMessage`](https://docs.inductiveautomation.com/display/DOC80/system.perspective.sendMessage) to broadcast back to the front-end. You could even have individual progress states this way.

---

<div class="post-metadata">

**Author:** ![YF129701](https://sea1.discourse-cdn.com/inductiveautomation/user_avatar/forum.inductiveautomation.com/yf129701/32/60498_2.png) [@YF129701](https://forum.inductiveautomation.com/u/YF129701)\
**Post date:** [September 30, 2020, 3:55pm UTC](https://forum.inductiveautomation.com/t/how-to-call-a-function-asynchronously-in-perspective-session/39229/3 "2020-09-30T15:55:46Z")

</div>

I guess I misunderstood the purpose of `system.util.invokeLater`. Does it just define what happens after the asynchronous function executes?

---

<div class="post-metadata">

**Author:** ![Kevin.Herron](https://sea1.discourse-cdn.com/inductiveautomation/user_avatar/forum.inductiveautomation.com/kevin.herron/32/7259_2.png) [@Kevin.Herron](https://forum.inductiveautomation.com/u/Kevin.Herron)\
**Post date:** [September 30, 2020, 4:07pm UTC](https://forum.inductiveautomation.com/t/how-to-call-a-function-asynchronously-in-perspective-session/39229/4 "2020-09-30T16:07:54Z")

</div>

`system.util.invokeLater` runs a function on the Swing EDT. It’s very special purpose for when you’re running code in the Vision Client or Designer and need to interact with the UI from a background thread.

---

<div class="post-metadata">

**Author:** ![MMaynard](https://sea1.discourse-cdn.com/inductiveautomation/user_avatar/forum.inductiveautomation.com/mmaynard/32/3366_2.png) [@MMaynard](https://forum.inductiveautomation.com/u/MMaynard)\
**Post date:** [September 30, 2020, 4:57pm UTC](https://forum.inductiveautomation.com/t/how-to-call-a-function-asynchronously-in-perspective-session/39229/5 "2020-09-30T16:57:36Z")

</div>

Not being completely up to speed on Perspective yet but…  
Could you create a gateway function that accepts a dataset and does the inserts?  
Then you could call the function and close the popup?  
Have the function write out to a status tag for success or failure and monitor that from the first view?

---

<div class="post-metadata">

**Author:** ![YF129701](https://sea1.discourse-cdn.com/inductiveautomation/user_avatar/forum.inductiveautomation.com/yf129701/32/60498_2.png) [@YF129701](https://forum.inductiveautomation.com/u/YF129701)\
**Post date:** [September 30, 2020, 9:40pm UTC](https://forum.inductiveautomation.com/t/how-to-call-a-function-asynchronously-in-perspective-session/39229/6 "2020-09-30T21:40:24Z")

</div>

I do do that, sort of. The actual API insert is a gateway function that accepts a few parameters.

Regarding a more general point, even if the function is on the gateway and it is called from an onActionPerformed script, doesn’t it still execute on one thread? @paul-griffith

Look at this example below I tried in the script console:

```auto
def printMessage():
    import time
    time.sleep(5) # wait for 5 seconds
    print "first"

printMessage()
print "second"

```

The above code first prints `"first"` and then `"second"`, as you would expect. However, it also waits for 5 seconds. It is identical to this:

```auto
import time
time.sleep(5)
print "first"
print "second"

```

@MMaynard you aren’t the first person who told me about this approach, though. My boss said he also uses it and I’m confused as to how that would be any different (offloading to gateway script or a function inside onActionPerformed script) from keeping all of the code in a script inline (like the second code snippet above).

Thanks in advance.

---

<div class="post-metadata">

**Author:** ![paul-griffith](https://sea1.discourse-cdn.com/inductiveautomation/user_avatar/forum.inductiveautomation.com/paul-griffith/32/37249_2.png) [@paul-griffith](https://forum.inductiveautomation.com/u/paul-griffith)\
**Post date:** [September 30, 2020, 9:59pm UTC](https://forum.inductiveautomation.com/t/how-to-call-a-function-asynchronously-in-perspective-session/39229/7 "2020-09-30T21:59:43Z")

</div>

Yes, writing it in a function vs writing it ‘inline’ is exactly the same - _but_ encapsulating it into a function encourages good practices, _and_ makes it trivial to call asynchronously. It’s also possible to ‘test’ business logic when it’s in functions (although in practice, Ignition does nothing to make that easier for you).

---

<div class="post-metadata">

**Author:** ![MMaynard](https://sea1.discourse-cdn.com/inductiveautomation/user_avatar/forum.inductiveautomation.com/mmaynard/32/3366_2.png) [@MMaynard](https://forum.inductiveautomation.com/u/MMaynard)\
**Post date:** [October 1, 2020, 11:52am UTC](https://forum.inductiveautomation.com/t/how-to-call-a-function-asynchronously-in-perspective-session/39229/8 "2020-10-01T11:52:06Z")

</div>

Paul,  
Lets say the function is in a global script library like

```auto
def DoSomething():
     #Following code extremely simplified for examples.
     ret = (collect data from dataset tag and insert code)
     #set flag to true or false on success/failure
    if ret:
        system.tag.write('[default]Global/InsertResult',1)
    else:
        system.tag.write('[default]Global/TriggerInsert',0)
    #Reset the trigger tag
    system.tag.write('[default]Global/TriggerInsert',0)

```

Then using a boolean memory tag script to trigger it

```auto
def valueChanged(tagPath, previousValue, currentValue, initialChange, missedEvents):
    if not initialChange and currentValue.value==1:
        global.doSomething()

```

Then on the popup write the values to a dataset tag and trigger the boolean tag to kick it off.  
Wouldn’t that prevent the GUI from being locked while the processing is happening?  
We use something like this for large downloads to the local system backends and it keeps the GUI from being taken over.

---

<div class="post-metadata">

**Author:** ![YF129701](https://sea1.discourse-cdn.com/inductiveautomation/user_avatar/forum.inductiveautomation.com/yf129701/32/60498_2.png) [@YF129701](https://forum.inductiveautomation.com/u/YF129701)\
**Post date:** [October 1, 2020, 3:09pm UTC](https://forum.inductiveautomation.com/t/how-to-call-a-function-asynchronously-in-perspective-session/39229/9 "2020-10-01T15:09:02Z")

</div>

Is thread creation and destruction done automatically (by OS, JVM or whatever it may be) or is there a command to let the same party know to close the thread? Will the thread be terminated upon the script inside it running completely or throwing an exception?

---

<div class="post-metadata">

**Author:** ![paul-griffith](https://sea1.discourse-cdn.com/inductiveautomation/user_avatar/forum.inductiveautomation.com/paul-griffith/32/37249_2.png) [@paul-griffith](https://forum.inductiveautomation.com/u/paul-griffith)\
**Post date:** [October 1, 2020, 3:35pm UTC](https://forum.inductiveautomation.com/t/how-to-call-a-function-asynchronously-in-perspective-session/39229/10 "2020-10-01T15:35:32Z")

</div>

> [@MMaynard](#):
>
> Wouldn’t that prevent the GUI from being locked while the processing is happening?

Yes, that's perfectly reasonable and is a true async invocation. There's a tradeoff; it's easier to trigger from either Vision or Perspective if you need to support both, but I would argue harder to reason about down the road - since you've got trigger logic separate from data separate from data handling code.  
However, no matter what you do, there's no way to put absolutely everything together, so I don't think there's any problems with that approach.

> [@YF129701](#):
>
> Is thread creation and destruction done automatically (by OS, JVM or whatever it may be) or is there a command to let the same party know to close the thread? Will the thread be terminated upon the script inside it running completely or throwing an exception?

8.0.11+ offers some visibility and ability to terminate threads: [Diagnostics - Running Scripts - Ignition User Manual 8.0 - Ignition Documentation](https://docs.inductiveautomation.com/display/DOC80/Diagnostics+-+Running+Scripts)  
And yes, the thread will automatically clean itself up once it doesn't have anything else to run - whether by a normal exit or an exception.
