# system.net.httpClient async use with callbacks v8.0.12

**URL:** <https://forum.inductiveautomation.com/t/system-net-httpclient-async-use-with-callbacks-v8-0-12/34320>\
**Category:** Ignition\
**Tags:** ignition80, scripting\
**Created:** [April 30, 2020, 1:21am UTC](https://forum.inductiveautomation.com/t/system-net-httpclient-async-use-with-callbacks-v8-0-12/34320 "2020-04-30T01:21:31Z")\
**Posts on this page:** 15\
**Page:** 1

<div class="post-metadata">

**Author:** ![simon.beeching](https://avatars.discourse-cdn.com/v4/letter/s/ac91a4/32.png) [@simon.beeching](https://forum.inductiveautomation.com/u/simon.beeching)\
**Post date:** [April 30, 2020, 1:21am UTC](https://forum.inductiveautomation.com/t/system-net-httpclient-async-use-with-callbacks-v8-0-12/34320/1 "2020-04-30T01:21:31Z")

</div>

I’m having some trouble with the Callback use.

```auto
	url = self.parent.parent.getChild('txt_URL_All').props.text
	
	client = system.net.httpClient()
	promise = client.getAsync("https://httpbin.org/delay/3",params={"a": 1, "b": 2})
	
	system.perspective.print("Assigning callback for call to: " + url)
	promise.whenComplete(recvDataCallback)
	#promise.get()
	system.perspective.print('Callback attached and we should get a triggered soon')

	
def recvDataCallback(arg):
#def recvDataCallback():
	system.perspective.print("Callback triggered")
	## do something out of band
	msgType = "ReadAllData"

```

 ![image](https://us1.discourse-cdn.com/inductiveautomation/original/2X/a/ad8af5ff6189f8c6382110f87fadc99e31cccf11.png)

I’m expecting to see that callback fired after 3 seconds, am I just doing this completely wrong?

---

<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:** [April 30, 2020, 1:57am UTC](https://forum.inductiveautomation.com/t/system-net-httpclient-async-use-with-callbacks-v8-0-12/34320/2 "2020-04-30T01:57:34Z")

</div>

I just think the print isn’t working because when the callback completes on a different thread there’s no implicit Perspective session for the print call to use.

If you stored the sessionId and then specified it when calling `system.perspective.print` it might work.

---

<div class="post-metadata">

**Author:** ![simon.beeching](https://avatars.discourse-cdn.com/v4/letter/s/ac91a4/32.png) [@simon.beeching](https://forum.inductiveautomation.com/u/simon.beeching)\
**Post date:** [April 30, 2020, 2:18am UTC](https://forum.inductiveautomation.com/t/system-net-httpclient-async-use-with-callbacks-v8-0-12/34320/3 "2020-04-30T02:18:48Z")

</div>

Unfortunately, that didn’t work either

```auto
	sessionId = self.session.props.id
	system.tag.write("localSessionId",sessionId)
	system.perspective.print("SessionId:" + sessionId)
	
	
	client = system.net.httpClient()
	promise = client.getAsync("https://httpbin.org/delay/3",params={"a": 1, "b": 2})
	
	system.perspective.print("Assigning callback for call to: " + url)
	promise.whenComplete(recvDataCallback)
	
	#promise.get()
	system.perspective.print('Callback attached and we should get a triggered soon')

	
def recvDataCallback(arg):
	sessionId = qv = system.tag.read("localSessionId")
	system.perspective.print("Callback triggered", sessionId=sessionId)

```

 ![image](https://us1.discourse-cdn.com/inductiveautomation/original/2X/2/22b0996434566da843ca47e5540aebaa232b308c.png)

and in the Firefox dev tools, the same thing  
 ![image](https://us1.discourse-cdn.com/inductiveautomation/original/2X/c/c28c1ae5f240586ff06e412cfe1027a5589bc103.png)

---

<div class="post-metadata">

**Author:** ![simon.beeching](https://avatars.discourse-cdn.com/v4/letter/s/ac91a4/32.png) [@simon.beeching](https://forum.inductiveautomation.com/u/simon.beeching)\
**Post date:** [April 30, 2020, 2:32am UTC](https://forum.inductiveautomation.com/t/system-net-httpclient-async-use-with-callbacks-v8-0-12/34320/4 "2020-04-30T02:32:18Z")

</div>

for interest sake, i replaced the memory tag with a Session Custom Property. No change in functionality, but probably should have used that in the first place.

---

<div class="post-metadata">

**Author:** ![simon.beeching](https://avatars.discourse-cdn.com/v4/letter/s/ac91a4/32.png) [@simon.beeching](https://forum.inductiveautomation.com/u/simon.beeching)\
**Post date:** [May 11, 2020, 4:57am UTC](https://forum.inductiveautomation.com/t/system-net-httpclient-async-use-with-callbacks-v8-0-12/34320/5 "2020-05-11T04:57:06Z")

</div>

Just to update, I’ve also tried writing to a memory tag in the callback with no luck.

---

<div class="post-metadata">

**Author:** ![kgamble](https://sea1.discourse-cdn.com/inductiveautomation/user_avatar/forum.inductiveautomation.com/kgamble/32/8019_2.png) [@kgamble](https://forum.inductiveautomation.com/u/kgamble)\
**Post date:** [May 11, 2020, 12:20pm UTC](https://forum.inductiveautomation.com/t/system-net-httpclient-async-use-with-callbacks-v8-0-12/34320/6 "2020-05-11T12:20:46Z")

</div>

This should do what you are looking for, when you are reaching the promise.whenComplete it is already finished evaluating and should return immediately, you are looking for your response.

promise.get() will block at then end though and wait for the response before continuing to execute

Also no need to worry about a tag, as you can use the sessionid as a parameter to your callback.

```auto
	def recvDataCallback(sessionId):
		system.perspective.print("Callback triggered", sessionId=sessionId)
			
	sessionId = self.session.props.id
	system.perspective.print("SessionId:" + sessionId)
		
	client = system.net.httpClient()
	promise = client.getAsync("https://httpbin.org/delay/3",params={"a": 1, "b": 2})
	
	system.perspective.print("Something after the initial call")
	
	response = promise.get()
	
	if response.isGood():
		recvDataCallback(sessionId)

```

I tested this out in Chrome and the Designer on a button

---

<div class="post-metadata">

**Author:** ![pturmel](https://sea1.discourse-cdn.com/inductiveautomation/user_avatar/forum.inductiveautomation.com/pturmel/32/3295_2.png) [@pturmel](https://forum.inductiveautomation.com/u/pturmel)\
**Post date:** [May 11, 2020, 12:45pm UTC](https://forum.inductiveautomation.com/t/system-net-httpclient-async-use-with-callbacks-v8-0-12/34320/7 "2020-05-11T12:45:49Z")

</div>

If you call promise.get() in the same thread as the .getAsync(), you’ve just converted the operation to synchronous. Equivalent to just using the blocking .get() method. If someone actually needs asynchronous operation, they can’t do this.

---

<div class="post-metadata">

**Author:** ![kgamble](https://sea1.discourse-cdn.com/inductiveautomation/user_avatar/forum.inductiveautomation.com/kgamble/32/8019_2.png) [@kgamble](https://forum.inductiveautomation.com/u/kgamble)\
**Post date:** [May 11, 2020, 1:51pm UTC](https://forum.inductiveautomation.com/t/system-net-httpclient-async-use-with-callbacks-v8-0-12/34320/8 "2020-05-11T13:51:27Z")

</div>

@pturmel is correct, what I provided will only work if you want to execute more scripting before dealing with your return in the same thread

If you want it to fully execute asynchronously then you are able to do that with the .getFuture method on the promise.

A few notes though:

1. You require a page ID in your callback, which I dont 100% understand since its in the same session?
2. I tried passing the function a parameter on the future.thenRun() however, Ignition does not like this and will give you a Null Pointer Exception (I assume it has something to do with the fact thenRun is expecting a java runnable, and not a python function call?)

Client script:

```auto
	def recvdCallbackFunction():
		tags = system.tag.readBlocking(["[default]localSessionId", "[default]localPageId"])
		system.perspective.print("Callback Triggered by function in Script", sessionId=tags[0].value, pageId=tags[1].value)
		system.perspective.print("Callback Triggered by function in Script without PageId", sessionId=tags[0].value)
	
	sessionId = self.session.props.id
	pageId = self.page.props.pageId
	system.tag.writeBlocking(["[default]localSessionId", "[default]localPageId"],[sessionId, pageId])
	system.perspective.print("SessionId:" + sessionId + ", PageId: " + pageId)
	
	client = system.net.httpClient()
	promise = client.getAsync("https://httpbin.org/delay/3",params={"a": 1, "b": 2})

	system.perspective.print("Assigning callback for call to: " + sessionId)

    #Get the CompletedFuture java class for the promise
	future = promise.getFuture()
    #Set the callback for your future response
	future.thenRun(recvdCallbackFunction)
	

```

---

<div class="post-metadata">

**Author:** ![kgamble](https://sea1.discourse-cdn.com/inductiveautomation/user_avatar/forum.inductiveautomation.com/kgamble/32/8019_2.png) [@kgamble](https://forum.inductiveautomation.com/u/kgamble)\
**Post date:** [May 11, 2020, 2:56pm UTC](https://forum.inductiveautomation.com/t/system-net-httpclient-async-use-with-callbacks-v8-0-12/34320/9 "2020-05-11T14:56:01Z")

</div>

Interesting update, it looks like if you reference the variables in your python script within the callback, they are still present and so you can actually execute this without tags involved. However I am not sure if this is intended functionality or not.

```auto
	def recvdCallbackFunction():
		system.perspective.print("Callback Triggered", sessionId=sessionId, pageId=pageId)
	
	sessionId = self.session.props.id
	pageId = self.page.props.pageId
	system.perspective.print("SessionId:" + sessionId + ", PageId: " + pageId)
	
	client = system.net.httpClient()
	promise = client.getAsync("https://httpbin.org/delay/3",params={"a": 1, "b": 2})

	system.perspective.print("Assigning callback for call to: " + sessionId)

	future = promise.getFuture()
	
	future.thenRun(recvdCallbackFunction)
	

```

---

<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:** [May 11, 2020, 3:03pm UTC](https://forum.inductiveautomation.com/t/system-net-httpclient-async-use-with-callbacks-v8-0-12/34320/10 "2020-05-11T15:03:02Z")

</div>

> [@kgamble](#):
>
> - You require a page ID in your callback, which I dont 100% understand since its in the same session?

It's because they're going to execute in different threads; httpClient async actions run in a dedicated pool of threads per-scope, and direct Perspective scripts run in a distinct pool of threads. Perspective scripting actions 'keep track' of what page, session, view, etc they're operating on via Java ThreadLocals - so as soon as you 'lose' the thread, you need to maintain that state yourself.

> - I tried passing the function a parameter on the future.thenRun() however, Ignition does not like this and will give you a Null Pointer Exception (I assume it has something to do with the fact thenRun is expecting a java runnable, and not a python function call?)

I'm interested in what this NPE is; Jython usually tries to automatically cast SAM (single abstract method) classes from functions. Either way, I'm not sure why [`Promise.then()`](http://files.inductiveautomation.com/sdk/javadoc/ignition80/8.0.12/com/inductiveautomation/ignition/common/script/builtin/http/Promise.html) or `whenComplete()` wouldn't work for these purposes; remember that your callback function should accept some argument, even if it's going to be ignored.

> [@kgamble](#):
>
> Interesting update, it looks like if you reference the variables in your python script within the callback, they are still present and so you can actually execute this without tags involved. However I am not sure if this is intended functionality or not.

That's intentional/part of the way Python does closures, yes. Another option if you want to be explicit about providing values to 'callback' functions that you don't have control of the call site is [`functools.partial`](https://docs.python.org/2.7/library/functools.html#functools.partial).

---

<div class="post-metadata">

**Author:** ![kgamble](https://sea1.discourse-cdn.com/inductiveautomation/user_avatar/forum.inductiveautomation.com/kgamble/32/8019_2.png) [@kgamble](https://forum.inductiveautomation.com/u/kgamble)\
**Post date:** [May 11, 2020, 3:38pm UTC](https://forum.inductiveautomation.com/t/system-net-httpclient-async-use-with-callbacks-v8-0-12/34320/11 "2020-05-11T15:38:11Z")

</div>

> [@paul-griffith](#):
>
> Either way, I’m not sure why [`Promise.then()`](http://files.inductiveautomation.com/sdk/javadoc/ignition80/8.0.12/com/inductiveautomation/ignition/common/script/builtin/http/Promise.html) or `whenComplete()` wouldn’t work for these purposes; remember that your callback function should accept some argument, even if it’s going to be ignored.

When I try to use

```auto
promise.then(recvdCallbackFunction) 
promise.whenComplete(recvdCallbackFunction)

```

They just never execute the function, error out, or create any gateway logs.

> [@paul-griffith](#):
>
> I’m interested in what this NPE is

Here is a script that throws it (It is extremely long), I just put it on a button in an 8.0.12 view. Interestingly it also executes the callback immediately without waiting for the response. You will see that all three statements are printed before the error happens.

```auto
	def recvdCallbackFunction(paramSessionId, paramPageId):
		system.perspective.print("Callback Triggered by function in Script", sessionId=paramSessionId, pageId=paramPageId)

	sessionId = self.session.props.id
	pageId = self.page.props.pageId
	system.perspective.print("SessionId:" + sessionId + ", PageId: " + pageId)
	
	client = system.net.httpClient()
	promise = client.getAsync("https://httpbin.org/delay/3",params={"a": 1, "b": 2})

	system.perspective.print("Assigning callback for call to: " + sessionId)

	future = promise.getFuture()
	
	future.thenRun(recvdCallbackFunction(sessionId, pageId))

```

---

<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:** [May 11, 2020, 3:43pm UTC](https://forum.inductiveautomation.com/t/system-net-httpclient-async-use-with-callbacks-v8-0-12/34320/12 "2020-05-11T15:43:49Z")

</div>

> [@kgamble](#):
>
> Interestingly it also executes the callback immediately without waiting for the response. You will see that all three statements are printed before the error happens.

Because you're calling the `recvdCallbackFunction` and passing its return result (nothing) to `thenRun`.

---

<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:** [May 11, 2020, 3:46pm UTC](https://forum.inductiveautomation.com/t/system-net-httpclient-async-use-with-callbacks-v8-0-12/34320/13 "2020-05-11T15:46:24Z")

</div>

I never updated, but when I played with this last week my takeaway was that the http client async functions work totally fine, but I never got system.perspective.print to work from another thread, even if I had the session id.

The callbacks were definitely happening though.

One of the fundamental problems with OP’s original code was that he defined the callback function _after_ referencing it, which everyone in subsequent posts, including me, seems to have fixed without mentioning explicitly.

---

<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:** [May 11, 2020, 6:50pm UTC](https://forum.inductiveautomation.com/t/system-net-httpclient-async-use-with-callbacks-v8-0-12/34320/14 "2020-05-11T18:50:59Z")

</div>

```python
	sessionId = self.session.props.id
	pageId = self.page.props.pageId
	
	def recvdCallbackFunction(result):
		system.perspective.print("Callback Triggered by function in Script", sessionId, pageId)
	
	system.perspective.print("SessionId:" + sessionId + ", PageId: " + pageId)
	
	client = system.net.httpClient()
	promise = client.getAsync("https://httpbin.org/delay/3", params={"a": 1, "b": 2})
	
	system.perspective.print("Assigning callback for call to: " + sessionId)
	
	promise.then(recvdCallbackFunction)

```

Works for me, without having to explicitly call anything blocking in the main Perspective worker thread.

---

<div class="post-metadata">

**Author:** ![simon.beeching](https://avatars.discourse-cdn.com/v4/letter/s/ac91a4/32.png) [@simon.beeching](https://forum.inductiveautomation.com/u/simon.beeching)\
**Post date:** [May 11, 2020, 10:46pm UTC](https://forum.inductiveautomation.com/t/system-net-httpclient-async-use-with-callbacks-v8-0-12/34320/15 "2020-05-11T22:46:18Z")

</div>

Thanks everyone, I was so focused on the callback side of things, I made an assumption about declaring the def in the wrong spot.

Kevin.Herron and PGriffith both made excellent points, and now I have what I wanted working. I’m extremely grateful for the assistance and hopefully this helps some other poor beginner too.
