# Trouble with httpClient .getAsync Callbacks

**URL:** <https://forum.inductiveautomation.com/t/trouble-with-httpclient-getasync-callbacks/81306>\
**Category:** Ignition\
**Tags:** ignition81\
**Created:** [November 1, 2023, 2:45pm UTC](https://forum.inductiveautomation.com/t/trouble-with-httpclient-getasync-callbacks/81306 "2023-11-01T14:45:57Z")\
**Posts on this page:** 20\
**Page:** 1

<div class="post-metadata">

**Author:** ![Cole\_Townsend](https://sea1.discourse-cdn.com/inductiveautomation/user_avatar/forum.inductiveautomation.com/cole_townsend/32/53149_2.png) [@Cole\_Townsend](https://forum.inductiveautomation.com/u/Cole_Townsend)\
**Post date:** [November 1, 2023, 2:45pm UTC](https://forum.inductiveautomation.com/t/trouble-with-httpclient-getasync-callbacks/81306/1 "2023-11-01T14:45:57Z")

</div>

Hey guys,

I am trying to set up some asynchronous API calls on my gateway but I am having trouble getting my callback function to run properly.

I have tried a few different things, but have been coming up short.  
I am trying to get each call of the callback function to write the resulting data into their respective tags that are living on my gateway. It feels as though the callback function isn't running at all, and it is hard to confirm that as I can't get loggers, prints, or anything else of the sort to run in the callback function, so I get no errors.

Within my callback function, I am also attempting to use payload data that exists in my for loop that is creating my asynchronous promise objects. I am not super familiar with python **closures** but as far as I know, this is how I should expect them to work.

Does anything in my code stick out to anyone that I am doing wrong? Or any advice on the correct way to debug callback functions like these in ignition?

```auto
def getCallback(response, error):
	rawData = {}
	for data in response.json['data']:
		for key in data:
			try:
				rawData[data[key]['name']] = data[key]['value']
			except:
				continue
	values = []
	paths = []
	for tag in system.tag.browse(payload['tagType']['fullPath'], filter = {'tagType':'AtomicTag'}).getResults():
		try:
			values.append(rawData[tag['name']])
		except:
			values.append(None)
		paths.append(str(tag['fullPath']))
		
	system.tag.writeBlocking(paths, values)
	return
	
def updateTags():
	basePath = '[default]TankLogix API Data/'
	token = system.tag.readBlocking(['[default]TankLogix API Data/Access Token'])
			
	headers = {
		'Authorization' : 'Bearer ' + token[0].value
	}
	client = system.net.httpClient()
	
	for Site in system.tag.browse(basePath, filter = {'tagType':'Folder'}).getResults():
		SiteID = system.tag.readBlocking([str(Site['fullPath']) + '/SiteID'])[0].value
		for TagType in system.tag.browse(Site['fullPath'], filter = {'tagType':'Folder'}).getResults():
			url = 'https://api.tanklogix.com/api/v2_1_0_0/SiteObjects/' + str(SiteID) + '/' + str(TagType['name'])
			p = client.getAsync(url = url, headers = headers)
			payload = {'tagType':TagType, 'siteID':SiteID}
			p.whenComplete(getCallback)

```

---

<div class="post-metadata">

**Author:** ![bkarabinchak.psi](https://sea1.discourse-cdn.com/inductiveautomation/user_avatar/forum.inductiveautomation.com/bkarabinchak.psi/32/83567_2.png) [@bkarabinchak.psi](https://forum.inductiveautomation.com/u/bkarabinchak.psi)\
**Post date:** [November 1, 2023, 3:03pm UTC](https://forum.inductiveautomation.com/t/trouble-with-httpclient-getasync-callbacks/81306/2 "2023-11-01T15:03:03Z")

</div>

Are you 100% sure it's not running at all? I would add some loggers to start, at least one in the beginning of the function saying that the function is starting.

It is possible the values you are getting from the http are identical to the current values and then it wouldn't look like anything is happening right?

---

<div class="post-metadata">

**Author:** ![Cole\_Townsend](https://sea1.discourse-cdn.com/inductiveautomation/user_avatar/forum.inductiveautomation.com/cole_townsend/32/53149_2.png) [@Cole\_Townsend](https://forum.inductiveautomation.com/u/Cole_Townsend)\
**Post date:** [November 1, 2023, 3:20pm UTC](https://forum.inductiveautomation.com/t/trouble-with-httpclient-getasync-callbacks/81306/3 "2023-11-01T15:20:44Z")

</div>

```auto
def getCallback(response, error):
	logger = system.util.getLogger("callbackFuntion")
	logger.info("Callback starting")
	
	...
	
	logger.info("Callback ending")
	return
	
def updateTags():
	logger = system.util.getLogger("updateTagsFuntion")
	logger.info("updateTags function starting")
	
	...
			
	logger.info("updateTags function ending")

```

 ![image](https://us1.discourse-cdn.com/inductiveautomation/original/3X/f/0/f0f2beb69b19012df9c5791973f0ffe7a141a8d0.png)

I'm also sure that the endpoint value is in fact changing because I can look at that data with a separate console as well.

---

<div class="post-metadata">

**Author:** ![bkarabinchak.psi](https://sea1.discourse-cdn.com/inductiveautomation/user_avatar/forum.inductiveautomation.com/bkarabinchak.psi/32/83567_2.png) [@bkarabinchak.psi](https://forum.inductiveautomation.com/u/bkarabinchak.psi)\
**Post date:** [November 1, 2023, 3:22pm UTC](https://forum.inductiveautomation.com/t/trouble-with-httpclient-getasync-callbacks/81306/4 "2023-11-01T15:22:07Z")

</div>

> [@Cole\_Townsend](#):
>
> `for TagType in system.tag.browse(Site['fullPath'], filter = {'tagType':'Folder'}).getResults():`

Is this loop producing any rows of data? I would put one in here to see that something is going on. I would probably put one log right now at each level of your loops just to see / be sure.

---

<div class="post-metadata">

**Author:** ![Cole\_Townsend](https://sea1.discourse-cdn.com/inductiveautomation/user_avatar/forum.inductiveautomation.com/cole_townsend/32/53149_2.png) [@Cole\_Townsend](https://forum.inductiveautomation.com/u/Cole_Townsend)\
**Post date:** [November 1, 2023, 3:26pm UTC](https://forum.inductiveautomation.com/t/trouble-with-httpclient-getasync-callbacks/81306/5 "2023-11-01T15:26:16Z")

</div>

I made a mistake, I didn't wait sufficiently long enough for the .whenComplete() to trigger and I turned the live logs off. doh

I guess my main issue is I don't know how closures work...

 ![image](https://us1.discourse-cdn.com/inductiveautomation/original/3X/6/7/678ab4cdc4bea73495589e35138e7225b84c7d55.png)

---

<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:** [November 1, 2023, 3:34pm UTC](https://forum.inductiveautomation.com/t/trouble-with-httpclient-getasync-callbacks/81306/6 "2023-11-01T15:34:53Z")

</div>

Closures capture variables for a function's use at the execution of the **def** , not at the point of the **call**. So no, your loop variables won't be available within your callback as shown.

Consider using lambda's within the `.whenComplete()` method to assemble the parts you need.

---

<div class="post-metadata">

**Author:** ![bkarabinchak.psi](https://sea1.discourse-cdn.com/inductiveautomation/user_avatar/forum.inductiveautomation.com/bkarabinchak.psi/32/83567_2.png) [@bkarabinchak.psi](https://forum.inductiveautomation.com/u/bkarabinchak.psi)\
**Post date:** [November 1, 2023, 3:35pm UTC](https://forum.inductiveautomation.com/t/trouble-with-httpclient-getasync-callbacks/81306/7 "2023-11-01T15:35:02Z")

</div>

> [@Cole\_Townsend](#):
>
> `for tag in system.tag.browse(payload['tagType']['fullPath'], filter = {'tagType':'AtomicTag'}).getResults():`

You have `payload` but this not defined anywhere before hand or fed as a parameter.

---

<div class="post-metadata">

**Author:** ![Cole\_Townsend](https://sea1.discourse-cdn.com/inductiveautomation/user_avatar/forum.inductiveautomation.com/cole_townsend/32/53149_2.png) [@Cole\_Townsend](https://forum.inductiveautomation.com/u/Cole_Townsend)\
**Post date:** [November 1, 2023, 4:23pm UTC](https://forum.inductiveautomation.com/t/trouble-with-httpclient-getasync-callbacks/81306/8 "2023-11-01T16:23:44Z")

</div>

main issue is I can't figure out how to feed it as a parameter into this callback function. the response and error parameters are passed by default with the whenComplete function, but not sure how to get the payload to go with it.

---

<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:** [November 1, 2023, 4:31pm UTC](https://forum.inductiveautomation.com/t/trouble-with-httpclient-getasync-callbacks/81306/9 "2023-11-01T16:31:53Z")

</div>

Maybe something like this:

```python
def callback(response, error, payload):
    # ...

def update():
    # ...

    payload = {}

    def inner_cb(response, error):
        callback(response, error, payload)

    promise = ...
    promise.whenComplete(inner_cb)

```

You can probably get fancier with `lambda` but this demonstrates the basic idea...

---

<div class="post-metadata">

**Author:** ![Cole\_Townsend](https://sea1.discourse-cdn.com/inductiveautomation/user_avatar/forum.inductiveautomation.com/cole_townsend/32/53149_2.png) [@Cole\_Townsend](https://forum.inductiveautomation.com/u/Cole_Townsend)\
**Post date:** [November 1, 2023, 4:40pm UTC](https://forum.inductiveautomation.com/t/trouble-with-httpclient-getasync-callbacks/81306/10 "2023-11-01T16:40:31Z")

</div>

Would this be the equivalent for a lambda function?  
 ![image](https://us1.discourse-cdn.com/inductiveautomation/original/3X/b/4/b42c9400785592cdf87df16951f20ccd0375e247.png)

---

<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:** [November 1, 2023, 4:46pm UTC](https://forum.inductiveautomation.com/t/trouble-with-httpclient-getasync-callbacks/81306/11 "2023-11-01T16:46:27Z")

</div>

You could also use [`functools.partial`](https://docs.python.org/2.7/library/functools.html#functools.partial):

```python
from functools import partial

def callback(response, error, payload):
    # ...

def update():
    # ...

    payload = {}

    inner_callback = partial(callback, payload=payload)

    promise = ...
    promise.whenComplete(inner_callback)

```

Lots of ways to skin this cat.

---

<div class="post-metadata">

**Author:** ![Cole\_Townsend](https://sea1.discourse-cdn.com/inductiveautomation/user_avatar/forum.inductiveautomation.com/cole_townsend/32/53149_2.png) [@Cole\_Townsend](https://forum.inductiveautomation.com/u/Cole_Townsend)\
**Post date:** [November 1, 2023, 4:58pm UTC](https://forum.inductiveautomation.com/t/trouble-with-httpclient-getasync-callbacks/81306/12 "2023-11-01T16:58:44Z")

</div>

New issue here:

 ![image](https://us1.discourse-cdn.com/inductiveautomation/original/3X/4/5/45abfe16771cb51fe99a758fa388b4eb1718dee2.png)

The callback function is only getting whatever the final payload is, instead of getting the correct payload for each call.

I'm assuming that the p.whenComplete(inner\_cb) is pulling the parameters at the time it runs the callback instead of at the time it runs the whenComplete method.

---

<div class="post-metadata">

**Author:** ![bkarabinchak.psi](https://sea1.discourse-cdn.com/inductiveautomation/user_avatar/forum.inductiveautomation.com/bkarabinchak.psi/32/83567_2.png) [@bkarabinchak.psi](https://forum.inductiveautomation.com/u/bkarabinchak.psi)\
**Post date:** [November 1, 2023, 5:02pm UTC](https://forum.inductiveautomation.com/t/trouble-with-httpclient-getasync-callbacks/81306/13 "2023-11-01T17:02:43Z")

</div>

Can you show the full script at this point? So we can make sure it's not just a scripting logic error.

---

<div class="post-metadata">

**Author:** ![Cole\_Townsend](https://sea1.discourse-cdn.com/inductiveautomation/user_avatar/forum.inductiveautomation.com/cole_townsend/32/53149_2.png) [@Cole\_Townsend](https://forum.inductiveautomation.com/u/Cole_Townsend)\
**Post date:** [November 1, 2023, 5:06pm UTC](https://forum.inductiveautomation.com/t/trouble-with-httpclient-getasync-callbacks/81306/14 "2023-11-01T17:06:03Z")

</div>

Logs (made some changes to what the payload carries):

 ![image](https://us1.discourse-cdn.com/inductiveautomation/original/3X/e/8/e822a465b7eaae1f807b42a33ae1cdd78b6154e8.png)

Code:

```auto
def getCallback(response, error, tagTypePath):
	logger = system.util.getLogger("callbackFunction")
	logger.info(tagTypePath)
	rawData = {}
	for data in response.json['data']:
		for key in data:
			try:
				rawData[data[key]['name']] = data[key]['value']
			except:
				continue
	values = []
	paths = []
	for tag in system.tag.browse(tagTypePath, filter = {'tagType':'AtomicTag'}).getResults():
		try:
			values.append(rawData[tag['name']])
		except:
			values.append(None)
		paths.append(str(tag['fullPath']))
		
	system.tag.writeBlocking(paths, values)
	return
	
def updateTags():
	logger = system.util.getLogger("updateTagsFunction")
	
	basePath = '[default]TankLogix API Data/'
	token = system.tag.readBlocking(['[default]TankLogix API Data/Access Token'])
			
	headers = {
		'Authorization' : 'Bearer ' + token[0].value
	}
	client = system.net.httpClient()
	
	for Site in system.tag.browse(basePath, filter = {'tagType':'Folder'}).getResults():
		SiteID = system.tag.readBlocking([str(Site['fullPath']) + '/SiteID'])[0].value
		for TagType in system.tag.browse(Site['fullPath'], filter = {'tagType':'Folder'}).getResults():
			url = 'https://api.tanklogix.com/api/v2_1_0_0/SiteObjects/' + str(SiteID) + '/' + str(TagType['name'])
			p = client.getAsync(url = url, headers = headers)
			tagTypePath = str(TagType['fullPath'])
			logger.info(tagTypePath)
			inner_cb = lambda response, error: getCallback(response, error, tagTypePath)
			p.whenComplete(inner_cb)

```

---

<div class="post-metadata">

**Author:** ![Cole\_Townsend](https://sea1.discourse-cdn.com/inductiveautomation/user_avatar/forum.inductiveautomation.com/cole_townsend/32/53149_2.png) [@Cole\_Townsend](https://forum.inductiveautomation.com/u/Cole_Townsend)\
**Post date:** [November 1, 2023, 7:55pm UTC](https://forum.inductiveautomation.com/t/trouble-with-httpclient-getasync-callbacks/81306/15 "2023-11-01T19:55:45Z")

</div>

> You could also use functools.partial

This seems to be the only way for me to skin this cat so far...

I've successfully gotten the appropriate payload data for each call of the function with this method.

 ![image](https://us1.discourse-cdn.com/inductiveautomation/original/3X/1/5/154e819faee06113f7879f2890c637c7fa9ca971.png)  
This is confirmed with live data now successfully being written to my tags.

I'm not sure what is up with using functions defined inside of the for loop, but as long as this works I'm willing to let that be someone else's mystery lol.

_ **Now that I've successfully gotten my asynchronous calls to actually run** _, I now have a related issue that caused me to work on this in the first place (there is actually a support ticket for this problem if any of you IA folks are interested #96751). I am having an issue with bounded sockets being allocated with each creation of a promise object, and they are not being released once the .whenComplete() method is run. Below is a chart of the count of bounded TCP sockets from running **netstat -aonq**

 ![image](https://us1.discourse-cdn.com/inductiveautomation/original/3X/8/8/884544f9a3ffc6599b3e9c5bdf79ebc67193b1d1.png)  
This eventually causes our server to exhaust all of its available ephemeral ports and starts causing all sorts of hard-to-diagnose issues.

Is there some method I am supposed to call to explicitly close these sockets? Or is that behavior that should be expected of the java service? Unless I find a way to close these sockets without a complete restart of the service/server, I can not use these asynchronous methods for httpClient.

Would be super interested to hear if anyone has any insight on this.

---

<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:** [November 1, 2023, 8:14pm UTC](https://forum.inductiveautomation.com/t/trouble-with-httpclient-getasync-callbacks/81306/16 "2023-11-01T20:14:30Z")

</div>

Are you instantiating a new `httpClient` for each call? if so, don't. Treat a single instance as a `session`, storing it in project library top-level variable so you can re-use it.

---

<div class="post-metadata">

**Author:** ![Cole\_Townsend](https://sea1.discourse-cdn.com/inductiveautomation/user_avatar/forum.inductiveautomation.com/cole_townsend/32/53149_2.png) [@Cole\_Townsend](https://forum.inductiveautomation.com/u/Cole_Townsend)\
**Post date:** [November 1, 2023, 8:16pm UTC](https://forum.inductiveautomation.com/t/trouble-with-httpclient-getasync-callbacks/81306/17 "2023-11-01T20:16:44Z")

</div>

Not for each http call, but yes for each updateTags() call, which is about 1 per minute. I am seeing about 8-9 new sockets per minute which lines up much better with the number of .getAsync() calls I am making.

I will try to instantiate the httpClient globally and see if that helps.

---

<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:** [November 1, 2023, 8:46pm UTC](https://forum.inductiveautomation.com/t/trouble-with-httpclient-getasync-callbacks/81306/18 "2023-11-01T20:46:04Z")

</div>

It would also be worth updating to 8.1.33, if you're not already there. We moved the platform to Java 17; there's specifically bug fixes for the builtin Java HTTP client that backs `system.net.httpClient`.

And _definitely_ avoid the creation of new `system.net.httpClient` instances as much as possible.

---

<div class="post-metadata">

**Author:** ![Cole\_Townsend](https://sea1.discourse-cdn.com/inductiveautomation/user_avatar/forum.inductiveautomation.com/cole_townsend/32/53149_2.png) [@Cole\_Townsend](https://forum.inductiveautomation.com/u/Cole_Townsend)\
**Post date:** [November 1, 2023, 10:57pm UTC](https://forum.inductiveautomation.com/t/trouble-with-httpclient-getasync-callbacks/81306/19 "2023-11-01T22:57:34Z")

</div>

Unfortunately, I have been on 8.1.33 for the last week now. I guess the specific bug fixes didn't have to do with closing forgotten httpClient's.

Is there a method in ignition where I would even be able to explicitly destroy a httpClient object? Or is the expected behavior for the client objects to clean themselves up at the end of a function scope?

---

<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:** [November 2, 2023, 3:09am UTC](https://forum.inductiveautomation.com/t/trouble-with-httpclient-getasync-callbacks/81306/20 "2023-11-02T03:09:17Z")

</div>

No. In 8.3.0 we're moving to Java 21, where it will be possible to explicitly close the underlying Java client we're creating for you, but not until then; it's a new feature of the Java client that there are apparently no plans to backport.

[Next page](https://forum.inductiveautomation.com/t/trouble-with-httpclient-getasync-callbacks/81306.md?page=2)
