While making some old web services (ASMX – ASp.Net) asynchronous, I ran into a problem with an error message in the browser after an ajax call to the service from javascript:
Error (RetrieveDD): {
"readyState": 4,
"responseText": "System.InvalidOperationException: An asynchronous operation cannot be started at this time. Asynchronous operations may only be started within an asynchronous handler or module or during certain events in the Page lifecycle. If this exception occurred while executing a Page, ensure that the Page is marked <%@ Page Async="true" %>. This exception may also indicate an attempt to call an "async void" method, which is generally unsupported within ASP.NET request processing. Instead, the asynchronous method should return a Task, and the caller should await it.rn at System.Web.AspNetSynchronizationContext.OperationStarted()rn at System.Runtime.CompilerServices.AsyncVoidMethodBuilder.Create()rn at SearchWebsite.DDService.LoadCountries(Int32 ID)rn",
"status": 500,
"statusText": "error"
}
The javascript side:
if (servicename != "") {
$.ajax({
url: '../../DDService.asmx/' + servicename,
dataType: 'json',
method: 'post',
data: { ID: id },
success: function success(data) {
ddl.empty();
if (hid != "") {
$.each(data, function () {
if (this['V'] == hid) {
var newOption = new Option(this['T'], this['V'], true, true);
ddl.append(newOption);
} else {
var newOption = new Option(this['T'], this['V'], false, false);
ddl.append(newOption);
}
});
} else {
$.each(data, function (index) {
if (index == 0) {
var newOption = new Option(this['T'], this['V'], true, true);
ddl.append(newOption);
} else {
var newOption = new Option(this['T'], this['V'], false, false);
ddl.append(newOption);
}
});
};
if (typeof callback === 'function') {
callback(data);
};
},
error: function error(err) {
console.log('Error (RetrieveDD): ' + JSON.stringify(err, null, 2));
if (typeof callback === 'function') {
callback(err);
}
}
});
};
And the method in the webservice (older asmx type):
[WebMethod(Description = "Return list of Countries")]
public async void LoadCountries(int ID)
{
var retval = new List<DDListItemLight>();
retval = await DropDownData.GetCountriesListAsync();
string responseStr = JsonConvert.SerializeObject(retval);
Context.Response.Write(responseStr);
}
Anyway, before I made the webmethod async, everything works just fine.
I’ve tried changing the signature to
public async Task
and
public async Task
as well with no luck.
I thought ending the response would work, but it has no effect.
While looking around here and on the internet, the suggestion is to put Async="true" on the page header – but this is a webmethod not an ASPX page, so I was hoping there was some way to make this work (with old asmx).
The MS documentation for this is a decade old and predates async/await (with no examples I could find)
Any ideas?
2
Answers
While the accepted answer worked, I decided to try something different and use controllers like with MVC. (ultimately will be converting over to Blazor WASM, so writing these controllers isn't a waste of time)
Here's the controller class:
Then called it from js like so:
ASMX does not support
async
. It was several generations old by the timeasync
was introduced, so it was never updated to supportasync
.It does support APM-style asynchrony, so you can get this to work, but it will be awkward.
First, you need to ensure you’re targeting .NET 4.5 or higher and have
httpRuntime.targetFramework
set to 4.5 or higher.Then, you can define your API using APM methods and wrap the more natural TAP methods. The wrapping can be a bit painful, so I recommend using the
ApmTaskFactory
from myAsyncEx
library, as such: