Should an async API ever throw synchronously?
Ultimately the decision to synchronously throw or not is up to you, and you will likely find people who argue either side. The important thing is to document the behavior and maintain consistency in the behavior.
My opinion on the matter is that your second option - passing the error into the callback - seems more elegant. Otherwise you end up with code that looks like this:
try {
getUserById(7, function (response) {
if (response.isSuccess) {
//Success case
} else {
//Failure case
}
});
} catch (error) {
//Other failure case
}
The control flow here is slightly confusing.
It seems like it would be better to have a single if / else if / else
structure in the callback and forgo the surrounding try / catch
.
Why do I need to await an async function when it is not supposedly returning a Promise?
All async
functions return a promise. All of them.
That promise will eventually resolve with whatever value you return from the async
function.
await
only blocks execution internal to the async
function. It does not block anything outside of the function. Conceptually, an async
function starts to execute and as soon as it hits an await
instruction, it immediately returns an unfulfilled promise (in the pending state) from the function and the outside execution world gets that promise and continues to execute.
Sometime later, the internal promise that was being await
ed will resolve and then the execution of the rest of the internals of the function will continue. Eventually the internals of the function will finish and return a value. That will trigger resolving the promise that was returned from the function with that return value.
FYI, there's a lot of superfluous stuff in your load()
function. You can change it from this:
async function load() {
const data = await new Promise(resolve => {
setTimeout(() => resolve([1, 2, 3]), 10);
}).then(data => data.map(i => i * 10));
console.log(`Data inside the function: ${JSON.stringify(data)}`);
return data;
}
to this:
function load() {
return new Promise(resolve => {
setTimeout(() => resolve([1, 2, 3]), 10);
}).then(data => data.map(i => i * 10));
}
Then, use it like this:
load().then(result => {
console.log(result);
});
Or, I prefer to encapsulate the manual creation of promise in their own function like this:
function delay(t, v) {
return new Promise(resolve => {
setTimeout(resolve.bind(null, v), t);
});
}
function load() {
return delay(10, [1, 2, 3]).then(data => data.map(i => i * 10));
}
And, it turns out this little delay()
function is generally useful in lots of places where you want to delay a promise chain.
Thanks to everyone participating and providing me with insight. But I'm still confused how should I be using
await
andasync
.
First off, most of the time you only mark a function async
if you need to use await
inside the function.
Second, you most commonly use await
(from within an async
function) when you have multiple asynchronous operations and you want to sequence them - often because the first one provides a result that is used as input to the second. You can use await
when all you have is a single asynchronous operation, but it doesn't really offer much of an advantage over a simple .then()
.
Here are a few examples of good reasons to use async
/await
:
Sequencing multiple asynchronous operations
Imagine you have getFromDatabase()
, getTheUrl()
and getTheContent()
that are all asynchronous. If any fails, you would want to just reject the returned promise with the first error.
Here's how this looks without async
/await
:
function run() {
return getFromDatabase(someArg).then(key => {
return getTheURL(key);
}).then(url => {
return getTheContent(url);
}).then(content => {
// some final processing
return finalValue;
});
}
Here's how this looks with async
/await
:
async function run(someArg) {
let key = await getFromDatabase(someArg);
let url = await getTheURL(key);
let content = await getTheContent(url);
// some final processing
return finalValue;
}
In both cases, the function returns a promise that resolves with the finalValue
so these two implementations are used the same by the caller:
run(someArg).then(finalValue => {
console.log(finalValue);
}).catch(err => {
console.log(err);
});
But, you will notice that the async
/await
implementation has more of a serialized, synchronous look to it and looks more like non-asynchronous code. Many find this easier to write, easier to read and easier to maintain. The more processing you have between steps, including branching, the more advantages accrue to the async
/await
version.
Automatically catching both rejected promises and synchronous exceptions
As I said earlier, async
functions always return a promise. They also have to built-in error handling that automatically propagates errors back to that returned promise.
It goes without saying that if you manually return a promise from the async
function and that promise rejects, then the promise returned from the async
function will reject.
But also, if you are using await
and any promise you are awaiting rejects and you don't have a .catch()
on the promise and don't have a try
/catch
around it, then the promise the function returns will automatically reject. So, back in our previous example of this:
async function run(someArg) {
let key = await getFromDatabase(someArg);
let url = await getTheURL(key);
let content = await getTheContent(url);
// some final processing
return finalValue;
}
If any of the three promises that are being awaited rejects, then the function will short circuit (stop executing any more code in the function) and reject the async
returned promise. So, you get this form of error handling for free.
Then lastly, an async
function also catches synchronous exceptions for you and turns them into a rejected promise.
In a normal function that returns a promise such as we had earlier:
function run() {
return getFromDatabase(someArg).then(key => {
return getTheURL(key);
}).then(url => {
return getTheContent(url);
}).then(content => {
// some final processing
return finalValue;
});
}
If getFromDatabase()
throws a synchronous exception (perhaps triggered because someArg
is invalid), then this overall function run()
will throw synchronously. That means that for the caller to catch all possible errors from run()
, they have to both surround it with a try
/catch
to catch the synchronous exceptions and use a .catch()
to catch the rejected promise:
try {
run(someArg).then(finalValue => {
console.log(finalValue);
}).catch(err => {
console.log(err);
});
} catch(e) {
console.log(err);
}
This is messy and a bit repetitive. But, when run()
is declared async
, then it will NEVER throw synchronously because any synchronous exception is automatically converted to a rejected promise so you can be sure you are capturing all possible errors when it's written this way:
async function run(someArg) {
let key = await getFromDatabase(someArg);
let url = await getTheURL(key);
let content = await getTheContent(url);
// some final processing
return finalValue;
}
// will catch all possible errors from run()
run(someArg).then(finalValue => {
console.log(finalValue);
}).catch(err => {
console.log(err);
});
Should I always call all of my function with an
await
?
First, you would only ever use await
with a function that returns a promise as await
offers no usefulness if the function does not return a promise (just adding to the clutter of your code if not needed).
Second, whether you use await
or not depends upon the context of both the calling function (since you HAVE to be in an async
function to use await
and on the flow of logic and whether it benefits from using await
or not.
Places where it's pointless to use await
async function getKey(someArg) {
let key = await getFromDatabase(someArg);
return key;
}
The await
here isn't doing anything useful. You're not sequencing multiple async
operations and you're not doing any processing on the return value. You can accomplish the exact same code by just returning the promise directly:
async function getKey(someArg) {
return getFromDatabase(someArg);
}
And, if you know that getFromDatabase()
never throws synchronously, you can even remove the async
from the declaration:
function getKey(someArg) {
return getFromDatabase(someArg);
}
Let's say I'm writing a code composed of multiple functions within multiple files. If I end up using a library which returns a Promise or it's an
async
function, should I trace back all my function calls from the asynchronous point to the entry point of the application and add anawait
before all the function calls after making themasync
?
This is a bit too general of an ask that's hard to answer in a general case. Here are some thoughts along this general direction:
Once any part of your result that you're trying to return from your function
A()
is asynchronous or uses any asynchronous operation to obtain, the function itself is asynchronous. In plain Javascript, you can never return an asynchronous result synchronously so your function must use an asynchronous method to return the result (promise, callback, event, etc...).Any function
B()
that calls your asynchronous functionA()
that is also trying to return a result based on what it gets fromA()
is now also asynchronous and also must communicate its result back using an asynchronous mechanism. This is true for a functionC()
that callsB()
and needs to communicate back its result to the caller. So, you can say that asynchronous behavior is infectious. Until you reach some point in the call chain where you no longer need to communicate back a result, everything has to use asynchronous mechanisms to communicate the result, error and completion.There's no specific need to mark a function
async
unless you specifically need one of the benefits of anasync
function such as the ability to useawait
inside that function or the automatic error handling it provides. You can write functions that returning promises just fine without usingasync
on the function declaration. So, "NO" I don't go back up the call chain making everythingasync
. I only make a functionasync
if there's a specific reason to do so. Usually that reason is that I want to useawait
inside the function, but there is also the automatic catching of synchronous exceptions that get turned into promise rejections that I described earlier. You would not generally need that with well behaved code, but it is sometimes useful with poorly behaved code orcode with an undefined behavior.await
is also only used when there's a specific reason for it. I don't just automatically use it on every function that returns a promise. I've described above reasons to use it. One can still use.then()
just fine for processing the result from a single function call that returns a promise. In some cases, it's just a matter of personal style whether you want to use.then()
orawait
and there is no particular reason it has to be one one way or the other.
Or maybe I should just get into the habit of calling all my functions with an
await
regardless of whether they areasync
or not?
Absolutely NOT! First off, the last thing you want to do is to take perfectly synchronous code and unnecessarily make it asynchronous or even make it look asynchronous. asynchronous code (even with async
and await
) is more complicated to write, debug, understand and maintain than synchronous code so you would never want to unnecessarily make synchronous code into asynchronous code by adding async
/await
into it:
For example, you would never do this:
async function random(min, max) {
let r = await Math.random();
return Math.floor((r * (max - min)) + min);
}
First off, this is a perfectly synchronous operation that can be coded like this:
function random(min, max) {
let r = Math.random();
return Math.floor((r * (max - min)) + min);
}
Second off, that first async
implementation has made the function a lot hard to use as it now has an asynchronous result:
random(1,10).then(r => {
console.log(r);
});
Instead of just the simple synchronous use:
console.log(random(1,10));
Should async function never ever throw?
Your assumptions on the async code are correct. See this post by Isaac Schlueter himself on the topic:
The pattern in node is that sync methods throw, and async methods pass
the error as the first argument to the callback. If the first
argument to your callback is falsey (usually null or undefined), then
all is well with the world.
http://groups.google.com/forum/#!msg/nodejs/W9UVJCKcJ7Q/rzseRbourCUJ
Trying to call Async method synchronously. It waits on Task.Result forever
This is a classic deadlock. The UI is waiting on the async method to finish, but the async method trys to update the UI thread and BOOM, deadlock.
Curiously though, if I copy this pattern more or less verbatim into a
Console app, it works.
That's because your WinForm application has a custom SynchronizationContext
. It is caputred implicitly and its job is to marshal work back onto the UI thread once returning from your await
.
Should you really expose synchronous wrappers around asynchronous operations?, the answer is no.
There is a way out of it, but i dont really like it. If you absolutely have to (you don't) call your code synchronously (again, you really shouldn't), use ConfigureAwait(false)
inside the async method. This instructs the awaitable
not to capture the current synccontext, so it wont marshal work back onto the UI thread:
public async Task<string> MyMethodAsync()
{
using (var cl = new HttpClient())
{
return await cl.GetStringAsync("http://www.google.co.uk/")
.ConfigureAwait(false);
}
}
Note that if you do this and then try to call any UI element afterwards, you'll end up with an InvalidOperationException
since you won't be on the UI thread.
Initializing the UI via a constructor is a common pattern. Stephan Cleary has a very nice series on async which you can find here.
What am I doing wrong? Is this simply a bad pattern, and if so, what
pattern should I be using instead?
Yes, absolutely. If you want to expose both asynchronous and synchronous APIs, use the proper api's which wouldn't get you into this situation (a deadlock) in the firstcase. For example, if you want to expose a synchronous DownloadString
, use WebClient
instead.
How to call asynchronous method from synchronous method in C#?
Asynchronous programming does "grow" through the code base. It has been compared to a zombie virus. The best solution is to allow it to grow, but sometimes that's not possible.
I have written a few types in my Nito.AsyncEx library for dealing with a partially-asynchronous code base. There's no solution that works in every situation, though.
Solution A
If you have a simple asynchronous method that doesn't need to synchronize back to its context, then you can use Task.WaitAndUnwrapException
:
var task = MyAsyncMethod();
var result = task.WaitAndUnwrapException();
You do not want to use Task.Wait
or Task.Result
because they wrap exceptions in AggregateException
.
This solution is only appropriate if MyAsyncMethod
does not synchronize back to its context. In other words, every await
in MyAsyncMethod
should end with ConfigureAwait(false)
. This means it can't update any UI elements or access the ASP.NET request context.
Solution B
If MyAsyncMethod
does need to synchronize back to its context, then you may be able to use AsyncContext.RunTask
to provide a nested context:
var result = AsyncContext.RunTask(MyAsyncMethod).Result;
*Update 4/14/2014: In more recent versions of the library the API is as follows:
var result = AsyncContext.Run(MyAsyncMethod);
(It's OK to use Task.Result
in this example because RunTask
will propagate Task
exceptions).
The reason you may need AsyncContext.RunTask
instead of Task.WaitAndUnwrapException
is because of a rather subtle deadlock possibility that happens on WinForms/WPF/SL/ASP.NET:
- A synchronous method calls an async method, obtaining a
Task
. - The synchronous method does a blocking wait on the
Task
. - The
async
method usesawait
withoutConfigureAwait
. - The
Task
cannot complete in this situation because it only completes when theasync
method is finished; theasync
method cannot complete because it is attempting to schedule its continuation to theSynchronizationContext
, and WinForms/WPF/SL/ASP.NET will not allow the continuation to run because the synchronous method is already running in that context.
This is one reason why it's a good idea to use ConfigureAwait(false)
within every async
method as much as possible.
Solution C
AsyncContext.RunTask
won't work in every scenario. For example, if the async
method awaits something that requires a UI event to complete, then you'll deadlock even with the nested context. In that case, you could start the async
method on the thread pool:
var task = Task.Run(async () => await MyAsyncMethod());
var result = task.WaitAndUnwrapException();
However, this solution requires a MyAsyncMethod
that will work in the thread pool context. So it can't update UI elements or access the ASP.NET request context. And in that case, you may as well add ConfigureAwait(false)
to its await
statements, and use solution A.
Update, 2019-05-01: The current "least-worst practices" are in an MSDN article here.
Async and await to synchronous method using Task
You can run asynchronous code in a synchronous manner by using the following extensions.
https://stackoverflow.com/a/5097066/5062791
public static class AsyncHelpers
{
/// <summary>
/// Execute's an async Task<T> method which has a void return value synchronously
/// </summary>
/// <param name="task">Task<T> method to execute</param>
public static void RunSync(Func<Task> task)
{
var oldContext = SynchronizationContext.Current;
var synch = new ExclusiveSynchronizationContext();
SynchronizationContext.SetSynchronizationContext(synch);
synch.Post(async _ =>
{
try
{
await task();
}
catch (Exception e)
{
synch.InnerException = e;
throw;
}
finally
{
synch.EndMessageLoop();
}
}, null);
synch.BeginMessageLoop();
SynchronizationContext.SetSynchronizationContext(oldContext);
}
/// <summary>
/// Execute's an async Task<T> method which has a T return type synchronously
/// </summary>
/// <typeparam name="T">Return Type</typeparam>
/// <param name="task">Task<T> method to execute</param>
/// <returns></returns>
public static T RunSync<T>(Func<Task<T>> task)
{
var oldContext = SynchronizationContext.Current;
var synch = new ExclusiveSynchronizationContext();
SynchronizationContext.SetSynchronizationContext(synch);
T ret = default(T);
synch.Post(async _ =>
{
try
{
ret = await task();
}
catch (Exception e)
{
synch.InnerException = e;
throw;
}
finally
{
synch.EndMessageLoop();
}
}, null);
synch.BeginMessageLoop();
SynchronizationContext.SetSynchronizationContext(oldContext);
return ret;
}
private class ExclusiveSynchronizationContext : SynchronizationContext
{
private bool done;
public Exception InnerException { get; set; }
readonly AutoResetEvent workItemsWaiting = new AutoResetEvent(false);
readonly Queue<Tuple<SendOrPostCallback, object>> items =
new Queue<Tuple<SendOrPostCallback, object>>();
public override void Send(SendOrPostCallback d, object state)
{
throw new NotSupportedException("We cannot send to our same thread");
}
public override void Post(SendOrPostCallback d, object state)
{
lock (items)
{
items.Enqueue(Tuple.Create(d, state));
}
workItemsWaiting.Set();
}
public void EndMessageLoop()
{
Post(_ => done = true, null);
}
public void BeginMessageLoop()
{
while (!done)
{
Tuple<SendOrPostCallback, object> task = null;
lock (items)
{
if (items.Count > 0)
{
task = items.Dequeue();
}
}
if (task != null)
{
task.Item1(task.Item2);
if (InnerException != null) // the method threw an exeption
{
throw new AggregateException("AsyncHelpers.Run method threw an exception.", InnerException);
}
}
else
{
workItemsWaiting.WaitOne();
}
}
}
public override SynchronizationContext CreateCopy()
{
return this;
}
}
}
How would I run an async TaskT method synchronously?
Here's a workaround I found that works for all cases (including suspended dispatchers). It's not my code and I'm still working to fully understand it, but it does work.
It can be called using:
customerList = AsyncHelpers.RunSync<List<Customer>>(() => GetCustomers());
Code is from here
public static class AsyncHelpers
{
/// <summary>
/// Execute's an async Task<T> method which has a void return value synchronously
/// </summary>
/// <param name="task">Task<T> method to execute</param>
public static void RunSync(Func<Task> task)
{
var oldContext = SynchronizationContext.Current;
var synch = new ExclusiveSynchronizationContext();
SynchronizationContext.SetSynchronizationContext(synch);
synch.Post(async _ =>
{
try
{
await task();
}
catch (Exception e)
{
synch.InnerException = e;
throw;
}
finally
{
synch.EndMessageLoop();
}
}, null);
synch.BeginMessageLoop();
SynchronizationContext.SetSynchronizationContext(oldContext);
}
/// <summary>
/// Execute's an async Task<T> method which has a T return type synchronously
/// </summary>
/// <typeparam name="T">Return Type</typeparam>
/// <param name="task">Task<T> method to execute</param>
/// <returns></returns>
public static T RunSync<T>(Func<Task<T>> task)
{
var oldContext = SynchronizationContext.Current;
var synch = new ExclusiveSynchronizationContext();
SynchronizationContext.SetSynchronizationContext(synch);
T ret = default(T);
synch.Post(async _ =>
{
try
{
ret = await task();
}
catch (Exception e)
{
synch.InnerException = e;
throw;
}
finally
{
synch.EndMessageLoop();
}
}, null);
synch.BeginMessageLoop();
SynchronizationContext.SetSynchronizationContext(oldContext);
return ret;
}
private class ExclusiveSynchronizationContext : SynchronizationContext
{
private bool done;
public Exception InnerException { get; set; }
readonly AutoResetEvent workItemsWaiting = new AutoResetEvent(false);
readonly Queue<Tuple<SendOrPostCallback, object>> items =
new Queue<Tuple<SendOrPostCallback, object>>();
public override void Send(SendOrPostCallback d, object state)
{
throw new NotSupportedException("We cannot send to our same thread");
}
public override void Post(SendOrPostCallback d, object state)
{
lock (items)
{
items.Enqueue(Tuple.Create(d, state));
}
workItemsWaiting.Set();
}
public void EndMessageLoop()
{
Post(_ => done = true, null);
}
public void BeginMessageLoop()
{
while (!done)
{
Tuple<SendOrPostCallback, object> task = null;
lock (items)
{
if (items.Count > 0)
{
task = items.Dequeue();
}
}
if (task != null)
{
task.Item1(task.Item2);
if (InnerException != null) // the method threw an exeption
{
throw new AggregateException("AsyncHelpers.Run method threw an exception.", InnerException);
}
}
else
{
workItemsWaiting.WaitOne();
}
}
}
public override SynchronizationContext CreateCopy()
{
return this;
}
}
}
Should I throw an error or return a rejected promise inside an async function?
They are correct.
The call to myCustomFunction
assumes that a promise is returned at all times (.then
and .catch
deal with resolved and rejected promises, respectively). When you throw an error, the function doesn't return a promise.
You could use this to catch the error:
try {
myModule.myCustomFunction(someInput).then(result => {
// carry on
})
.catch(err => {
// do something with the error
})
} catch(err) {
...
}
But as you can see, this results in two error handlers: try/catch
for the synchronously thrown error, and .catch
for any rejected promises that sns.createTopic(someParams)
may return.
That's why it's better to use Promise.reject()
:
module.exports.myCustomFunction = input => {
if (badInput) {
return Promise.reject('failed');
}
return sns.createTopic(someParams).promise()
}
Related Topics
Scroll to Element on Click in Angular 4
How to Clear a Chart from a Canvas So That Hover Events Cannot Be Triggered
How to Make a Button Redirect My Page to Another Page
JavaScript to Detect If User Changes Tab
Createelement VS. Createelementns
Detect If User Clicks Inside a Circle
Open Android Application from a Web Page
How to Filter Input Type="File" Dialog by Specific File Type
How to Retrieve If the Popstate Event Comes from Back or Forward Actions with the HTML5 Pushstate
Addeventlistener for Keydown on Canvas
Format Date in a Specific Timezone
How to Get the Anchor from the Url Using Jquery
How to Post with Multipart Form Data Using Fetch
How to Detect If Flash Is Installed and If Not, Display a Hidden Div That Informs the User
How to Find the Array Index with a Value
Expose Jquery to Real Window Object with Webpack