SlideShare ist ein Scribd-Unternehmen logo
1 von 46
Async vs Coroutines:
Best Practices
Johannes Ahvenniemi
Lead Programmer, Seriously Digital Entertainment
About me
3
— Started with: BASIC
— First published “indie” game: Java
— First professional job: ActionScript 3 (Flash)
— Favourite language: C++
— More tools in the box: C#, Clojure, Lua, JavaScript,
OpenGL…
— Unity developer since 2014
— Struggling with “callback hell”
Coroutines
5
— Useful for spanning logic across multiple frames
— Cooperative multitasking (Single threaded)
— Clever utilisation of C# generators (IEnumerators)
— This talk is about Unity’s Coroutines, not generators
— Familiar from Go, Lua, Python and many other languages
async/await
6
— Not intended for replacing Coroutines
— Useful for handling non-blocking IO (and long lasting routines,
i.e. threads)
— Old C# feature (originating from F# (originating from Haskell))
— Now also in JavaScript, Python, Dart, C++, Rust…
— Finally introduced in Unity 2017
Comparison
Coroutine Async
Doesn’t have return values Has return values
Can’t run synchronously Can run synchronously
Doesn’t work without the engine Works without the engine
Doesn’t support try/catch Supports try/catch
Doesn’t preserve call stack Preserves call stack
Always shows exceptions Can hide exceptions
Doesn’t always exit Always exits
Lifetime tied to a MonoBehaviour Lifetime handled manually
Familiar to most Unity developers Unfamiliar to many Unity developers
Coroutine Async
Doesn’t have return values Has return values
Can’t run synchronously Can run synchronously
Doesn’t work without the engine Works without the engine
Doesn’t support try/catch Supports try/catch
Doesn’t preserve call stack Preserves call stack
Always shows exceptions Can hide exceptions
Doesn’t always exit Always exits
Lifetime tied to a MonoBehaviour Lifetime handled manually
Familiar to most Unity developers Unfamiliar to many Unity developers
Coroutine Async
Doesn’t have return values Has return values
Can’t run synchronously Can run synchronously
Doesn’t work without the engine Works without the engine
Doesn’t support try/catch Supports try/catch
Doesn’t preserve call stack Preserves call stack
Always shows exceptions Can hide exceptions
Doesn’t always exit Always exits
Lifetime tied to a MonoBehaviour Lifetime handled manually
Familiar to most Unity developers Unfamiliar to many Unity developers
Coroutine Async
Doesn’t have return values Has return values
Can’t run synchronously Can run synchronously
Doesn’t work without the engine Works without the engine
Doesn’t support try/catch Supports try/catch
Doesn’t preserve call stack Preserves call stack
Always shows exceptions Can hide exceptions
Doesn’t always exit Always exits
Lifetime tied to a MonoBehaviour Lifetime handled manually
Familiar to most Unity developers Unfamiliar to many Unity developers
10
Disclaimer: The following examples aren’t
production quality. They are full of memory leaks
and bad practices. Don’t just copy these examples
into your product!
Coroutine
Doesn’t have return values
IEnumerator Start()
{
IEnumerator enumerator = SelectButton();
yield return enumerator;
Button selectedButton = (Button)enumerator.Current;
Assert.IsTrue(selectedButton != null);
Debug.Log(selectedButton.name);
}
static IEnumerator SelectButton()
{
Button selectedButton = null;
Button[] buttons = FindObjectsOfType<Button>();
foreach (Button button in buttons)
{
Button b = button;
b.onClick.AddListener(() => selectedButton = b);
}
while (selectedButton == null)
{
yield return null;
}
yield return selectedButton;
}
11
Async
Has return values
async void Start()
{
Button selectedButton = await SelectButton();
Debug.Log(selectedButton.name);
}
static async Task<Button> SelectButton()
{
Button[] buttons = FindObjectsOfType<Button>();
var tasks = buttons.Select(PressButton);
Task<Button> finishedTask = await Task.WhenAny(tasks);
return finishedTask.Result;
}
static async Task<Button> PressButton(Button button)
{
bool isPressed = false;
button.onClick.AddListener(() => isPressed = true);
while (!isPressed)
{
await Task.Yield();
}
return button;
}
Coroutine Async
Doesn’t have return values Has return values
Can’t run synchronously Can run synchronously
Doesn’t work without the engine Works without the engine
Doesn’t support try/catch Supports try/catch
Doesn’t preserve call stack Preserves call stack
Always shows exceptions Can hide exceptions
Doesn’t always exit Always exits
Lifetime tied to a MonoBehaviour Lifetime handled manually
Familiar to most Unity developers Unfamiliar to many Unity developers
Coroutine Async
Doesn’t have return values Has return values
Can’t run synchronously Can run synchronously
Doesn’t work without the engine Works without the engine
Doesn’t support try/catch Supports try/catch
Doesn’t preserve call stack Preserves call stack
Always shows exceptions Can hide exceptions
Doesn’t always exit Always exits
Lifetime tied to a MonoBehaviour Lifetime handled manually
Familiar to most Unity developers Unfamiliar to many Unity developers
Coroutine
Can’t run synchronously
IEnumerator Start()
{
Debug.Log($"Frame: {Time.frameCount}");
yield return Function();
Debug.Log($"Frame: {Time.frameCount}");
}
static IEnumerator Function()
{
if (false)
yield return null;
}
13
Async
Can run synchronously
async void Start()
{
Debug.Log($"Frame: {Time.frameCount}");
await Function();
Debug.Log($"Frame: {Time.frameCount}");
}
static async Task Function()
{
if (false)
await Task.Yield();
}
Outputs:
Frame: 1
Frame: 2
Outputs:
Frame: 1
Frame: 1
Coroutine Async
Doesn’t have return values Has return values
Can’t run synchronously Can run synchronously
Doesn’t work without the engine Works without the engine
Doesn’t support try/catch Supports try/catch
Doesn’t preserve call stack Preserves call stack
Always shows exceptions Can hide exceptions
Doesn’t always exit Always exits
Lifetime tied to a MonoBehaviour Lifetime handled manually
Familiar to most Unity developers Unfamiliar to many Unity developers
Coroutine Async
Doesn’t have return values Has return values
Can’t run synchronously Can run synchronously
Doesn’t work without the engine Works without the engine
Doesn’t support try/catch Supports try/catch
Doesn’t preserve call stack Preserves call stack
Always shows exceptions Can hide exceptions
Doesn’t always exit Always exits
Lifetime tied to a MonoBehaviour Lifetime handled manually
Familiar to most Unity developers Unfamiliar to many Unity developers
15
Coroutine Async
Doesn’t have return values Has return values
Can’t run synchronously Can run synchronously
Doesn’t work without the engine Works without the engine
Doesn’t support try/catch Supports try/catch
Doesn’t preserve call stack Preserves call stack
Always shows exceptions Can hide exceptions
Doesn’t always exit Always exits
Lifetime tied to a MonoBehaviour Lifetime handled manually
Familiar to most Unity developers Unfamiliar to many Unity developers
Coroutine Async
Doesn’t have return values Has return values
Can’t run synchronously Can run synchronously
Doesn’t work without the engine Works without the engine
Doesn’t support try/catch Supports try/catch
Doesn’t preserve call stack Preserves call stack
Always shows exceptions Can hide exceptions
Doesn’t always exit Always exits
Lifetime tied to a MonoBehaviour Lifetime handled manually
Familiar to most Unity developers Unfamiliar to many Unity developers
Coroutine
Doesn’t support try/catch
public class Root : MonoBehaviour
{
IEnumerator Start()
{
try
{
yield return MainMenu();
}
catch (Exception)
{
SceneManager.LoadScene("Error");
throw;
}
}
}
17
Async
Supports try/catch
public class Root : MonoBehaviour
{
async void Start()
{
try
{
await MainMenu();
}
catch (Exception)
{
SceneManager.LoadScene("Error");
throw;
}
}
}
Compiler error! Works!
Coroutine Async
Doesn’t have return values Has return values
Can’t run synchronously Can run synchronously
Doesn’t work without the engine Works without the engine
Doesn’t support try/catch Supports try/catch
Doesn’t preserve call stack Preserves call stack
Always shows exceptions Can hide exceptions
Doesn’t always exit Always exits
Lifetime tied to a MonoBehaviour Lifetime handled manually
Familiar to most Unity developers Unfamiliar to many Unity developers
Coroutine Async
Doesn’t have return values Has return values
Can’t run synchronously Can run synchronously
Doesn’t work without the engine Works without the engine
Doesn’t support try/catch Supports try/catch
Doesn’t preserve call stack Preserves call stack
Always shows exceptions Can hide exceptions
Doesn’t always exit Always exits
Lifetime tied to a MonoBehaviour Lifetime handled manually
Familiar to most Unity developers Unfamiliar to many Unity developers
19
Coroutine
Doesn’t preserve call stack
Async
Preserves call stack
20
Coroutine
Doesn’t preserve call stack
Async
Preserves call stack
21
Coroutine
Doesn’t preserve call stack
Async
Preserves call stack
22
Async call stack in the profiler
:(
23
Mind the stack!
Watch out for asset
references in the stack!
UnloadUnusedAssets will unload the assets but might cause surprises
My suggestion: Don’t await non-additive scenes from non-additive scenes
Coroutine Async
Doesn’t have return values Has return values
Can’t run synchronously Can run synchronously
Doesn’t work without the engine Works without the engine
Doesn’t support try/catch Supports try/catch
Doesn’t preserve call stack Preserves call stack
Always shows exceptions Can hide exceptions
Doesn’t always exit Always exits
Lifetime tied to a MonoBehaviour Lifetime handled manually
Familiar to most Unity developers Unfamiliar to many Unity developers
Coroutine Async
Doesn’t have return values Has return values
Can’t run synchronously Can run synchronously
Doesn’t work without the engine Works without the engine
Doesn’t support try/catch Supports try/catch
Doesn’t preserve call stack Preserves call stack
Always shows exceptions Can hide exceptions
Doesn’t always exit Always exits
Lifetime tied to a MonoBehaviour Lifetime handled manually
Familiar to most Unity developers Unfamiliar to many Unity developers
Coroutine
Always shows exceptions
void Start()
{
this.StartCoroutine(Function());
}
static IEnumerator Function()
{
yield return null;
Debug.Log("About to throw exception");
throw new Exception();
}
25
Async
Can hide exceptions
void Start()
{
Function();
}
static async Task Function()
{
await Task.Yield();
Debug.Log("About to throw exception");
throw new Exception();
}
Coroutine Async
Doesn’t have return values Has return values
Can’t run synchronously Can run synchronously
Doesn’t work without the engine Works without the engine
Doesn’t support try/catch Supports try/catch
Doesn’t preserve call stack Preserves call stack
Always shows exceptions Can hide exceptions
Doesn’t always exit Always exits
Lifetime tied to a MonoBehaviour Lifetime handled manually
Familiar to most Unity developers Unfamiliar to many Unity developers
Coroutine Async
Doesn’t have return values Has return values
Can’t run synchronously Can run synchronously
Doesn’t work without the engine Works without the engine
Doesn’t support try/catch Supports try/catch
Doesn’t preserve call stack Preserves call stack
Always shows exceptions Can hide exceptions
Doesn’t always exit Always exits
Lifetime tied to a MonoBehaviour Lifetime handled manually
Familiar to most Unity developers Unfamiliar to many Unity developers
Coroutine
Doesn’t always exit
IEnumerator ShowEffect(RawImage container)
{
var texture = new RenderTexture(512, 512, 0);
try
{
container.texture = texture;
for (int i = 0; i < 100; ++i)
{
/*
* Update effect
*/
yield return null;
}
}
finally
{
texture.Release();
}
}
27
Async
Always exits
async Task ShowEffect(RawImage container)
{
var texture = new RenderTexture(512, 512, 0);
try
{
container.texture = texture;
for (int i = 0; i < 100; ++i)
{
/*
* Update effect
*/
await Task.Yield();
}
}
finally
{
texture.Release();
}
}
Leaks if owning
MonoBehaviour is destroyed!
Never leaks!
Coroutine Async
Doesn’t have return values Has return values
Can’t run synchronously Can run synchronously
Doesn’t work without the engine Works without the engine
Doesn’t support try/catch Supports try/catch
Doesn’t preserve call stack Preserves call stack
Always shows exceptions Can hide exceptions
Doesn’t always exit Always exits
Lifetime tied to a MonoBehaviour Lifetime handled manually
Familiar to most Unity developers Unfamiliar to many Unity developers
Coroutine Async
Doesn’t have return values Has return values
Can’t run synchronously Can run synchronously
Doesn’t work without the engine Works without the engine
Doesn’t support try/catch Supports try/catch
Doesn’t preserve call stack Preserves call stack
Always shows exceptions Can hide exceptions
Doesn’t always exit Always exits
Lifetime tied to a MonoBehaviour Lifetime handled manually
Familiar to most Unity developers Unfamiliar to many Unity developers
Coroutine
Lifetime tied to a MonoBehaviour
IEnumerator Start()
{
this.StartCoroutine(this.Rotate());
yield return new WaitForSeconds(1.0f);
Destroy(gameObject);
}
IEnumerator Rotate()
{
while (true)
{
transform.Rotate(Vector3.forward, 1.0f);
yield return null;
}
}
29
Async
Lifetime handled manually
async void Start()
{
this.Rotate();
await Task.Delay(1000);
Destroy(gameObject);
}
async void Rotate()
{
while (true)
{
transform.Rotate(Vector3.forward, 1.0f);
await Task.Yield();
}
}
Throws NullRef Exception!Stops silently!
30
Async
Lifetime handled manually
Fixed? No.
async void Start()
{
var cts = new CancellationTokenSource();
this.Rotate(cts.Token);
await Task.Delay(1000);
cts.Cancel();
Destroy(this.gameObject);
}
async void Rotate(CancellationToken ct)
{
while (!ct.IsCancellationRequested)
{
this.transform.Rotate(Vector3.forward, 1.0f);
await Task.Yield();
}
}
Coroutine Async
Doesn’t have return values Has return values
Can’t run synchronously Can run synchronously
Doesn’t work without the engine Works without the engine
Doesn’t support try/catch Supports try/catch
Doesn’t preserve call stack Preserves call stack
Always shows exceptions Can hide exceptions
Doesn’t always exit Always exits
Lifetime tied to a MonoBehaviour Lifetime handled manually
Familiar to most Unity developers Unfamiliar to many Unity developers
Coroutine Async
Doesn’t have return values Has return values
Can’t run synchronously Can run synchronously
Doesn’t work without the engine Works without the engine
Doesn’t support try/catch Supports try/catch
Doesn’t preserve call stack Preserves call stack
Always shows exceptions Can hide exceptions
Doesn’t always exit Always exits
Lifetime tied to a MonoBehaviour Lifetime handled manually
Familiar to most Unity developers Unfamiliar to many Unity developers
Example:
Creating a prompt popup
32
33
public class PromptPopup1 : MonoBehaviour
{
public Button buttonAccept;
public Button buttonCancel;
public Button buttonClose;
public static async Task<bool> Spawn()
{
PromptPopup1 p = await Utils.LoadScene<PromptPopup1>("Popup", LoadSceneMode.Additive);
Button selected = await Utils.SelectButton(p.buttonAccept, p.buttonCancel, p.buttonClose);
return selectedButton == p.buttonAccept;
}
}
34
Simple prompt popup
public class PromptPopup2 : MonoBehaviour
{
public Button buttonAccept;
public Button buttonCancel;
public Button buttonClose;
public static async Task<bool> Spawn()
{
PromptPopup2 p = await Utils.LoadScene<PromptPopup2>("Popup", LoadSceneMode.Additive);
Task<Button> selectButtonTask = Utils.SelectButton(p.buttonAccept, p.buttonCancel, p.buttonClose);
Task pressBackButtonTask = Utils.WaitForPressBackButton();
Task finishedTask = await Task.WhenAny(selectButtonTask, pressBackButtonTask);
await finishedTask; // Propagate exception if the task finished because of exception
return finishedTask == selectButtonTask && selectButtonTask.Result == p.buttonAccept;
}
}
35
Implement Android back button
36
Implement Android back button
public class PromptPopup2 : MonoBehaviour
{
public Button buttonAccept;
public Button buttonCancel;
public Button buttonClose;
public static async Task<bool> Spawn()
{
PromptPopup2 p = await Utils.LoadScene<PromptPopup2>("Popup", LoadSceneMode.Additive);
Task<Button> selectButtonTask = Utils.SelectButton(p.buttonAccept, p.buttonCancel, p.buttonClose);
Task pressBackButtonTask = Utils.WaitForPressBackButton();
Task finishedTask = await Task.WhenAny(selectButtonTask, pressBackButtonTask);
await finishedTask; // Propagate exception if the task finished because of exception
return finishedTask == selectButtonTask && selectButtonTask.Result == p.buttonAccept;
}
}
public class PromptPopup3 : MonoBehaviour
{
public Button buttonAccept;
public Button buttonCancel;
public Button buttonClose;
public static async Task<bool> Spawn(CancellationToken ct)
{
PromptPopup3 p = await Utils.LoadScene<PromptPopup3>("Popup", LoadSceneMode.Additive, ct);
var selectButtonTask = Utils.SelectButton(ct, p.buttonAccept, p.buttonCancel, p.buttonClose);
var pressBackButtonTask = Utils.WaitForPressBackButton(ct);
var finishedTask = await Task.WhenAny(selectButtonTask, pressBackButtonTask);
await finishedTask; // Propagate exception if the task finished because of exception
return finishedTask == selectButtonTask && selectButtonTask.Result == p.buttonAccept;
}
}
37
Implement manual cancellation
public class PromptPopup4 : MonoBehaviour
{
public Button buttonAccept;
public Button buttonCancel;
public Button buttonClose;
public static async Task<bool> Spawn(CancellationToken ct)
{
PromptPopup4 p = await Utils.LoadScene<PromptPopup4>("Popup", LoadSceneMode.Additive, ct);
var linkedCts = CancellationTokenSource.CreateLinkedTokenSource(ct);
var linkedCt = linkedCts.Token;
var buttonTask = Utils.SelectButton(linkedCt, p.buttonAccept, p.buttonCancel, p.buttonClose);
var pressBackButtonTask = Utils.WaitForPressBackButton(linkedCt);
var finishedTask = await Task.WhenAny(buttonTask, pressBackButtonTask);
await finishedTask; // Propagate exception if the task finished because of exception
linkedCts.Cancel();
return finishedTask == buttonTask && buttonTask.Result == p.buttonAccept;
}
} 38
Clean up remaining tasks
public class PromptPopup5 : MonoBehaviour
{
public Button buttonAccept;
public Button buttonCancel;
public Button buttonClose;
public static async Task<bool> Spawn(CancellationToken ct)
{
PromptPopup5 p = await Utils.LoadScene<PromptPopup5>("Popup", LoadSceneMode.Additive, ct);
using (var linkedCts = CancellationTokenSource.CreateLinkedTokenSource(ct))
{
var linkedCt = linkedCts.Token;
var buttonTask = Utils.SelectButton(linkedCt, p.buttonAccept, p.buttonCancel, p.buttonClose);
var pressBackButtonTask = Utils.WaitForPressBackButton(linkedCt);
var finishedTask = await Task.WhenAny(buttonTask, pressBackButtonTask);
await finishedTask; // Propagate exception if the task finished because of exception
linkedCts.Cancel();
return finishedTask == buttonTask && buttonTask.Result == p.buttonAccept;
}
}
} 39
Dispose linked CancellationTokenSource
public static async Task<bool> Spawn(CancellationToken ct)
{
PromptPopup6 p = await Utils.LoadScene<PromptPopup6>("Popup", LoadSceneMode.Additive, ct);
var linkedCts = CancellationTokenSource.CreateLinkedTokenSource(ct);
try
{
var linkedCt = linkedCts.Token;
var buttonTask = Utils.SelectButton(linkedCt, p.buttonAccept, p.buttonCancel, p.buttonClose);
var pressBackButtonTask = Utils.WaitForPressBackButton(linkedCt);
var finishedTask = await Task.WhenAny(buttonTask, pressBackButtonTask);
await finishedTask; // Propagate exception if the task finished because of exception
linkedCts.Cancel();
return finishedTask == buttonTask && buttonTask.Result == popup.buttonAccept;
}
finally
{
linkedCts.Dispose();
popup.StartCoroutine(Utils.PlayPopupCloseAnimation(p.gameObject));
}
}
40
Implement closing the popup
public static async Task<bool> Spawn(CancellationToken ct)
{
PromptPopup6 p = await Utils.LoadScene<PromptPopup6>("Popup", LoadSceneMode.Additive, ct);
var linkedCts = CancellationTokenSource.CreateLinkedTokenSource(ct);
try
{
var linkedCt = linkedCts.Token;
var buttonTask = Utils.SelectButton(linkedCt, p.buttonAccept, p.buttonCancel, p.buttonClose);
var pressBackButtonTask = Utils.WaitForPressBackButton(linkedCt);
var finishedTask = await Task.WhenAny(buttonTask, pressBackButtonTask);
await finishedTask; // Propagate exception if the task finished because of exception
linkedCts.Cancel();
return finishedTask == buttonTask && buttonTask.Result == popup.buttonAccept;
}
finally
{
linkedCts.Dispose();
popup.StartCoroutine(Utils.PlayPopupCloseAnimation(p.gameObject));
}
}
41
Implement closing the popup
42
Choosing between Async
and Coroutines
43
Choosing between Async and Coroutines
Rules of Thumb
44
1. Use async when you deal with IO (User input, Network calls)
2. Use coroutines for fire-and-forget routines.
3. Don’t intertwine them
Thank you!
Johannes Ahvenniemi
Lead Programmer, Seriously Digital Entertainment
johannes@seriously.com
We are hiring!
Johannes Ahvenniemi
Lead Programmer, Seriously Digital Entertainment
johannes@seriously.com

Weitere ähnliche Inhalte

Was ist angesagt?

[Unite2015 박민근] 유니티 최적화 테크닉 총정리
[Unite2015 박민근] 유니티 최적화 테크닉 총정리[Unite2015 박민근] 유니티 최적화 테크닉 총정리
[Unite2015 박민근] 유니티 최적화 테크닉 총정리MinGeun Park
 
[NDC17] Unreal.js - 자바스크립트로 쉽고 빠른 UE4 개발하기
[NDC17] Unreal.js - 자바스크립트로 쉽고 빠른 UE4 개발하기[NDC17] Unreal.js - 자바스크립트로 쉽고 빠른 UE4 개발하기
[NDC17] Unreal.js - 자바스크립트로 쉽고 빠른 UE4 개발하기현철 조
 
ECS (Part 1/3) - Introduction to Data-Oriented Design
ECS (Part 1/3) - Introduction to Data-Oriented DesignECS (Part 1/3) - Introduction to Data-Oriented Design
ECS (Part 1/3) - Introduction to Data-Oriented DesignPhuong Hoang Vu
 
전형규, SilvervineUE4Lua: UE4에서 Lua 사용하기, NDC2019
전형규, SilvervineUE4Lua: UE4에서 Lua 사용하기, NDC2019전형규, SilvervineUE4Lua: UE4에서 Lua 사용하기, NDC2019
전형규, SilvervineUE4Lua: UE4에서 Lua 사용하기, NDC2019devCAT Studio, NEXON
 
Pitfalls of Object Oriented Programming by SONY
Pitfalls of Object Oriented Programming by SONYPitfalls of Object Oriented Programming by SONY
Pitfalls of Object Oriented Programming by SONYAnaya Medias Swiss
 
온라인 게임 처음부터 끝까지 동적언어로 만들기
온라인 게임 처음부터 끝까지 동적언어로 만들기온라인 게임 처음부터 끝까지 동적언어로 만들기
온라인 게임 처음부터 끝까지 동적언어로 만들기Seungjae Lee
 
리플렉션과 가비지 컬렉션
리플렉션과 가비지 컬렉션리플렉션과 가비지 컬렉션
리플렉션과 가비지 컬렉션QooJuice
 
[데브루키/141206 박민근] 유니티 최적화 테크닉 총정리
[데브루키/141206 박민근] 유니티 최적화 테크닉 총정리[데브루키/141206 박민근] 유니티 최적화 테크닉 총정리
[데브루키/141206 박민근] 유니티 최적화 테크닉 총정리MinGeun Park
 
[150124 박민근] 모바일 게임 개발에서 루아 스크립트 활용하기
[150124 박민근] 모바일 게임 개발에서 루아 스크립트 활용하기[150124 박민근] 모바일 게임 개발에서 루아 스크립트 활용하기
[150124 박민근] 모바일 게임 개발에서 루아 스크립트 활용하기MinGeun Park
 
NDC11_슈퍼클래스
NDC11_슈퍼클래스NDC11_슈퍼클래스
NDC11_슈퍼클래스noerror
 
레퍼런스만 알면 언리얼 엔진이 제대로 보인다
레퍼런스만 알면 언리얼 엔진이 제대로 보인다레퍼런스만 알면 언리얼 엔진이 제대로 보인다
레퍼런스만 알면 언리얼 엔진이 제대로 보인다Lee Dustin
 
임태현, 게임 서버 디자인 가이드, NDC2013
임태현, 게임 서버 디자인 가이드, NDC2013임태현, 게임 서버 디자인 가이드, NDC2013
임태현, 게임 서버 디자인 가이드, NDC2013devCAT Studio, NEXON
 
190119 unreal engine c++ 입문 및 팁
190119 unreal engine c++ 입문 및 팁190119 unreal engine c++ 입문 및 팁
190119 unreal engine c++ 입문 및 팁KWANGIL KIM
 
East Coast DevCon 2014: Concurrency & Parallelism in UE4 - Tips for programmi...
East Coast DevCon 2014: Concurrency & Parallelism in UE4 - Tips for programmi...East Coast DevCon 2014: Concurrency & Parallelism in UE4 - Tips for programmi...
East Coast DevCon 2014: Concurrency & Parallelism in UE4 - Tips for programmi...Gerke Max Preussner
 
테라로 살펴본 MMORPG의 논타겟팅 시스템
테라로 살펴본 MMORPG의 논타겟팅 시스템테라로 살펴본 MMORPG의 논타겟팅 시스템
테라로 살펴본 MMORPG의 논타겟팅 시스템QooJuice
 
[NDC2017] 뛰는 프로그래머 나는 언리얼 엔진 - 언알못에서 커미터까지
[NDC2017] 뛰는 프로그래머 나는 언리얼 엔진 - 언알못에서 커미터까지[NDC2017] 뛰는 프로그래머 나는 언리얼 엔진 - 언알못에서 커미터까지
[NDC2017] 뛰는 프로그래머 나는 언리얼 엔진 - 언알못에서 커미터까지Minjung Ko
 
[야생의 땅: 듀랑고] 서버 아키텍처 Vol. 2 (자막)
[야생의 땅: 듀랑고] 서버 아키텍처 Vol. 2 (자막)[야생의 땅: 듀랑고] 서버 아키텍처 Vol. 2 (자막)
[야생의 땅: 듀랑고] 서버 아키텍처 Vol. 2 (자막)Heungsub Lee
 
『DirectX 12를 이용한 3D 게임 프로그래밍 입문』 - 맛보기
『DirectX 12를 이용한 3D 게임 프로그래밍 입문』 - 맛보기『DirectX 12를 이용한 3D 게임 프로그래밍 입문』 - 맛보기
『DirectX 12를 이용한 3D 게임 프로그래밍 입문』 - 맛보기복연 이
 
AAA게임_UI_최적화_및_빌드하기.pptx
AAA게임_UI_최적화_및_빌드하기.pptxAAA게임_UI_최적화_및_빌드하기.pptx
AAA게임_UI_최적화_및_빌드하기.pptxTonyCms
 
심예람, <프로젝트DH> AI 내비게이션 시스템, NDC2018
심예람, <프로젝트DH> AI 내비게이션 시스템, NDC2018심예람, <프로젝트DH> AI 내비게이션 시스템, NDC2018
심예람, <프로젝트DH> AI 내비게이션 시스템, NDC2018devCAT Studio, NEXON
 

Was ist angesagt? (20)

[Unite2015 박민근] 유니티 최적화 테크닉 총정리
[Unite2015 박민근] 유니티 최적화 테크닉 총정리[Unite2015 박민근] 유니티 최적화 테크닉 총정리
[Unite2015 박민근] 유니티 최적화 테크닉 총정리
 
[NDC17] Unreal.js - 자바스크립트로 쉽고 빠른 UE4 개발하기
[NDC17] Unreal.js - 자바스크립트로 쉽고 빠른 UE4 개발하기[NDC17] Unreal.js - 자바스크립트로 쉽고 빠른 UE4 개발하기
[NDC17] Unreal.js - 자바스크립트로 쉽고 빠른 UE4 개발하기
 
ECS (Part 1/3) - Introduction to Data-Oriented Design
ECS (Part 1/3) - Introduction to Data-Oriented DesignECS (Part 1/3) - Introduction to Data-Oriented Design
ECS (Part 1/3) - Introduction to Data-Oriented Design
 
전형규, SilvervineUE4Lua: UE4에서 Lua 사용하기, NDC2019
전형규, SilvervineUE4Lua: UE4에서 Lua 사용하기, NDC2019전형규, SilvervineUE4Lua: UE4에서 Lua 사용하기, NDC2019
전형규, SilvervineUE4Lua: UE4에서 Lua 사용하기, NDC2019
 
Pitfalls of Object Oriented Programming by SONY
Pitfalls of Object Oriented Programming by SONYPitfalls of Object Oriented Programming by SONY
Pitfalls of Object Oriented Programming by SONY
 
온라인 게임 처음부터 끝까지 동적언어로 만들기
온라인 게임 처음부터 끝까지 동적언어로 만들기온라인 게임 처음부터 끝까지 동적언어로 만들기
온라인 게임 처음부터 끝까지 동적언어로 만들기
 
리플렉션과 가비지 컬렉션
리플렉션과 가비지 컬렉션리플렉션과 가비지 컬렉션
리플렉션과 가비지 컬렉션
 
[데브루키/141206 박민근] 유니티 최적화 테크닉 총정리
[데브루키/141206 박민근] 유니티 최적화 테크닉 총정리[데브루키/141206 박민근] 유니티 최적화 테크닉 총정리
[데브루키/141206 박민근] 유니티 최적화 테크닉 총정리
 
[150124 박민근] 모바일 게임 개발에서 루아 스크립트 활용하기
[150124 박민근] 모바일 게임 개발에서 루아 스크립트 활용하기[150124 박민근] 모바일 게임 개발에서 루아 스크립트 활용하기
[150124 박민근] 모바일 게임 개발에서 루아 스크립트 활용하기
 
NDC11_슈퍼클래스
NDC11_슈퍼클래스NDC11_슈퍼클래스
NDC11_슈퍼클래스
 
레퍼런스만 알면 언리얼 엔진이 제대로 보인다
레퍼런스만 알면 언리얼 엔진이 제대로 보인다레퍼런스만 알면 언리얼 엔진이 제대로 보인다
레퍼런스만 알면 언리얼 엔진이 제대로 보인다
 
임태현, 게임 서버 디자인 가이드, NDC2013
임태현, 게임 서버 디자인 가이드, NDC2013임태현, 게임 서버 디자인 가이드, NDC2013
임태현, 게임 서버 디자인 가이드, NDC2013
 
190119 unreal engine c++ 입문 및 팁
190119 unreal engine c++ 입문 및 팁190119 unreal engine c++ 입문 및 팁
190119 unreal engine c++ 입문 및 팁
 
East Coast DevCon 2014: Concurrency & Parallelism in UE4 - Tips for programmi...
East Coast DevCon 2014: Concurrency & Parallelism in UE4 - Tips for programmi...East Coast DevCon 2014: Concurrency & Parallelism in UE4 - Tips for programmi...
East Coast DevCon 2014: Concurrency & Parallelism in UE4 - Tips for programmi...
 
테라로 살펴본 MMORPG의 논타겟팅 시스템
테라로 살펴본 MMORPG의 논타겟팅 시스템테라로 살펴본 MMORPG의 논타겟팅 시스템
테라로 살펴본 MMORPG의 논타겟팅 시스템
 
[NDC2017] 뛰는 프로그래머 나는 언리얼 엔진 - 언알못에서 커미터까지
[NDC2017] 뛰는 프로그래머 나는 언리얼 엔진 - 언알못에서 커미터까지[NDC2017] 뛰는 프로그래머 나는 언리얼 엔진 - 언알못에서 커미터까지
[NDC2017] 뛰는 프로그래머 나는 언리얼 엔진 - 언알못에서 커미터까지
 
[야생의 땅: 듀랑고] 서버 아키텍처 Vol. 2 (자막)
[야생의 땅: 듀랑고] 서버 아키텍처 Vol. 2 (자막)[야생의 땅: 듀랑고] 서버 아키텍처 Vol. 2 (자막)
[야생의 땅: 듀랑고] 서버 아키텍처 Vol. 2 (자막)
 
『DirectX 12를 이용한 3D 게임 프로그래밍 입문』 - 맛보기
『DirectX 12를 이용한 3D 게임 프로그래밍 입문』 - 맛보기『DirectX 12를 이용한 3D 게임 프로그래밍 입문』 - 맛보기
『DirectX 12를 이용한 3D 게임 프로그래밍 입문』 - 맛보기
 
AAA게임_UI_최적화_및_빌드하기.pptx
AAA게임_UI_최적화_및_빌드하기.pptxAAA게임_UI_최적화_및_빌드하기.pptx
AAA게임_UI_최적화_및_빌드하기.pptx
 
심예람, <프로젝트DH> AI 내비게이션 시스템, NDC2018
심예람, <프로젝트DH> AI 내비게이션 시스템, NDC2018심예람, <프로젝트DH> AI 내비게이션 시스템, NDC2018
심예람, <프로젝트DH> AI 내비게이션 시스템, NDC2018
 

Ähnlich wie Best practices: Async vs. coroutines - Unite Copenhagen 2019

Async in C# - The Good, the Bad and the Ugly
Async in C# - The Good, the Bad and the UglyAsync in C# - The Good, the Bad and the Ugly
Async in C# - The Good, the Bad and the UglyStuart Lang
 
Android design patterns
Android design patternsAndroid design patterns
Android design patternsVitali Pekelis
 
Codeception Testing Framework -- English #phpkansai
Codeception Testing Framework -- English #phpkansaiCodeception Testing Framework -- English #phpkansai
Codeception Testing Framework -- English #phpkansaiFlorent Batard
 
Puppet Camp Düsseldorf 2014: Continuously Deliver Your Puppet Code with Jenki...
Puppet Camp Düsseldorf 2014: Continuously Deliver Your Puppet Code with Jenki...Puppet Camp Düsseldorf 2014: Continuously Deliver Your Puppet Code with Jenki...
Puppet Camp Düsseldorf 2014: Continuously Deliver Your Puppet Code with Jenki...Puppet
 
Puppet Camp Duesseldorf 2014: Toni Schmidbauer - Continuously deliver your pu...
Puppet Camp Duesseldorf 2014: Toni Schmidbauer - Continuously deliver your pu...Puppet Camp Duesseldorf 2014: Toni Schmidbauer - Continuously deliver your pu...
Puppet Camp Duesseldorf 2014: Toni Schmidbauer - Continuously deliver your pu...NETWAYS
 
Dead-Simple Async Control Flow with Coroutines
Dead-Simple Async Control Flow with CoroutinesDead-Simple Async Control Flow with Coroutines
Dead-Simple Async Control Flow with CoroutinesTravis Kaufman
 
How to make a large C++-code base manageable
How to make a large C++-code base manageableHow to make a large C++-code base manageable
How to make a large C++-code base manageablecorehard_by
 
Node.js exception handling
Node.js exception handlingNode.js exception handling
Node.js exception handlingMinh Hoang
 
.Net Squad. Deployments. Workflow. Namics
.Net Squad. Deployments. Workflow. Namics.Net Squad. Deployments. Workflow. Namics
.Net Squad. Deployments. Workflow. NamicsDaniel Scherrer
 
Debugging Tips and Tricks - iOS Conf Singapore 2015
Debugging Tips and Tricks - iOS Conf Singapore 2015Debugging Tips and Tricks - iOS Conf Singapore 2015
Debugging Tips and Tricks - iOS Conf Singapore 2015Fahim Farook
 
Pair programming pair testing working together with the developers by Simon ...
Pair programming  pair testing working together with the developers by Simon ...Pair programming  pair testing working together with the developers by Simon ...
Pair programming pair testing working together with the developers by Simon ...Agile ME
 
Construir Aplicações Silverlight para Windows Phone 7
Construir Aplicações Silverlight para Windows Phone 7Construir Aplicações Silverlight para Windows Phone 7
Construir Aplicações Silverlight para Windows Phone 7Comunidade NetPonto
 
Puppeteer - A web scraping & UI Testing Tool
Puppeteer - A web scraping & UI Testing ToolPuppeteer - A web scraping & UI Testing Tool
Puppeteer - A web scraping & UI Testing ToolMiki Lombardi
 
Docker: automation for the rest of us
Docker: automation for the rest of usDocker: automation for the rest of us
Docker: automation for the rest of usJérôme Petazzoni
 
Keyboard_Kung_Fu
Keyboard_Kung_FuKeyboard_Kung_Fu
Keyboard_Kung_FuCraig Angus
 
BUILDING APPS WITH ASYNCIO
BUILDING APPS WITH ASYNCIOBUILDING APPS WITH ASYNCIO
BUILDING APPS WITH ASYNCIOMykola Novik
 
Charm City Linux - Jan 2014 - Web Dev Made Easy - Shell Revolution
Charm City Linux - Jan 2014 - Web Dev Made Easy - Shell RevolutionCharm City Linux - Jan 2014 - Web Dev Made Easy - Shell Revolution
Charm City Linux - Jan 2014 - Web Dev Made Easy - Shell RevolutionChris Stone
 
From Generator to Fiber the Road to Coroutine in PHP
From Generator to Fiber the Road to Coroutine in PHPFrom Generator to Fiber the Road to Coroutine in PHP
From Generator to Fiber the Road to Coroutine in PHPAlbert Chen
 

Ähnlich wie Best practices: Async vs. coroutines - Unite Copenhagen 2019 (20)

Async in C# - The Good, the Bad and the Ugly
Async in C# - The Good, the Bad and the UglyAsync in C# - The Good, the Bad and the Ugly
Async in C# - The Good, the Bad and the Ugly
 
Android design patterns
Android design patternsAndroid design patterns
Android design patterns
 
Codeception Testing Framework -- English #phpkansai
Codeception Testing Framework -- English #phpkansaiCodeception Testing Framework -- English #phpkansai
Codeception Testing Framework -- English #phpkansai
 
Puppet Camp Düsseldorf 2014: Continuously Deliver Your Puppet Code with Jenki...
Puppet Camp Düsseldorf 2014: Continuously Deliver Your Puppet Code with Jenki...Puppet Camp Düsseldorf 2014: Continuously Deliver Your Puppet Code with Jenki...
Puppet Camp Düsseldorf 2014: Continuously Deliver Your Puppet Code with Jenki...
 
Puppet Camp Duesseldorf 2014: Toni Schmidbauer - Continuously deliver your pu...
Puppet Camp Duesseldorf 2014: Toni Schmidbauer - Continuously deliver your pu...Puppet Camp Duesseldorf 2014: Toni Schmidbauer - Continuously deliver your pu...
Puppet Camp Duesseldorf 2014: Toni Schmidbauer - Continuously deliver your pu...
 
Dead-Simple Async Control Flow with Coroutines
Dead-Simple Async Control Flow with CoroutinesDead-Simple Async Control Flow with Coroutines
Dead-Simple Async Control Flow with Coroutines
 
How to make a large C++-code base manageable
How to make a large C++-code base manageableHow to make a large C++-code base manageable
How to make a large C++-code base manageable
 
Node.js exception handling
Node.js exception handlingNode.js exception handling
Node.js exception handling
 
.Net Squad. Deployments. Workflow. Namics
.Net Squad. Deployments. Workflow. Namics.Net Squad. Deployments. Workflow. Namics
.Net Squad. Deployments. Workflow. Namics
 
Debugging Tips and Tricks - iOS Conf Singapore 2015
Debugging Tips and Tricks - iOS Conf Singapore 2015Debugging Tips and Tricks - iOS Conf Singapore 2015
Debugging Tips and Tricks - iOS Conf Singapore 2015
 
Pair programming pair testing working together with the developers by Simon ...
Pair programming  pair testing working together with the developers by Simon ...Pair programming  pair testing working together with the developers by Simon ...
Pair programming pair testing working together with the developers by Simon ...
 
Construir Aplicações Silverlight para Windows Phone 7
Construir Aplicações Silverlight para Windows Phone 7Construir Aplicações Silverlight para Windows Phone 7
Construir Aplicações Silverlight para Windows Phone 7
 
Puppeteer - A web scraping & UI Testing Tool
Puppeteer - A web scraping & UI Testing ToolPuppeteer - A web scraping & UI Testing Tool
Puppeteer - A web scraping & UI Testing Tool
 
Docker: automation for the rest of us
Docker: automation for the rest of usDocker: automation for the rest of us
Docker: automation for the rest of us
 
Tiad - Docker: Automation for the rest of us
Tiad - Docker: Automation for the rest of usTiad - Docker: Automation for the rest of us
Tiad - Docker: Automation for the rest of us
 
Keyboard_Kung_Fu
Keyboard_Kung_FuKeyboard_Kung_Fu
Keyboard_Kung_Fu
 
Unity3D Programming
Unity3D ProgrammingUnity3D Programming
Unity3D Programming
 
BUILDING APPS WITH ASYNCIO
BUILDING APPS WITH ASYNCIOBUILDING APPS WITH ASYNCIO
BUILDING APPS WITH ASYNCIO
 
Charm City Linux - Jan 2014 - Web Dev Made Easy - Shell Revolution
Charm City Linux - Jan 2014 - Web Dev Made Easy - Shell RevolutionCharm City Linux - Jan 2014 - Web Dev Made Easy - Shell Revolution
Charm City Linux - Jan 2014 - Web Dev Made Easy - Shell Revolution
 
From Generator to Fiber the Road to Coroutine in PHP
From Generator to Fiber the Road to Coroutine in PHPFrom Generator to Fiber the Road to Coroutine in PHP
From Generator to Fiber the Road to Coroutine in PHP
 

Mehr von Unity Technologies

Build Immersive Worlds in Virtual Reality
Build Immersive Worlds  in Virtual RealityBuild Immersive Worlds  in Virtual Reality
Build Immersive Worlds in Virtual RealityUnity Technologies
 
Augmenting reality: Bring digital objects into the real world
Augmenting reality: Bring digital objects into the real worldAugmenting reality: Bring digital objects into the real world
Augmenting reality: Bring digital objects into the real worldUnity Technologies
 
Let’s get real: An introduction to AR, VR, MR, XR and more
Let’s get real: An introduction to AR, VR, MR, XR and moreLet’s get real: An introduction to AR, VR, MR, XR and more
Let’s get real: An introduction to AR, VR, MR, XR and moreUnity Technologies
 
Using synthetic data for computer vision model training
Using synthetic data for computer vision model trainingUsing synthetic data for computer vision model training
Using synthetic data for computer vision model trainingUnity Technologies
 
The Tipping Point: How Virtual Experiences Are Transforming Global Industries
The Tipping Point: How Virtual Experiences Are Transforming Global IndustriesThe Tipping Point: How Virtual Experiences Are Transforming Global Industries
The Tipping Point: How Virtual Experiences Are Transforming Global IndustriesUnity Technologies
 
Unity Roadmap 2020: Live games
Unity Roadmap 2020: Live games Unity Roadmap 2020: Live games
Unity Roadmap 2020: Live games Unity Technologies
 
Unity Roadmap 2020: Core Engine & Creator Tools
Unity Roadmap 2020: Core Engine & Creator ToolsUnity Roadmap 2020: Core Engine & Creator Tools
Unity Roadmap 2020: Core Engine & Creator ToolsUnity Technologies
 
How ABB shapes the future of industry with Microsoft HoloLens and Unity - Uni...
How ABB shapes the future of industry with Microsoft HoloLens and Unity - Uni...How ABB shapes the future of industry with Microsoft HoloLens and Unity - Uni...
How ABB shapes the future of industry with Microsoft HoloLens and Unity - Uni...Unity Technologies
 
Unity XR platform has a new architecture – Unite Copenhagen 2019
Unity XR platform has a new architecture – Unite Copenhagen 2019Unity XR platform has a new architecture – Unite Copenhagen 2019
Unity XR platform has a new architecture – Unite Copenhagen 2019Unity Technologies
 
Turn Revit Models into real-time 3D experiences
Turn Revit Models into real-time 3D experiencesTurn Revit Models into real-time 3D experiences
Turn Revit Models into real-time 3D experiencesUnity Technologies
 
How Daimler uses mobile mixed realities for training and sales - Unite Copenh...
How Daimler uses mobile mixed realities for training and sales - Unite Copenh...How Daimler uses mobile mixed realities for training and sales - Unite Copenh...
How Daimler uses mobile mixed realities for training and sales - Unite Copenh...Unity Technologies
 
How Volvo embraced real-time 3D and shook up the auto industry- Unite Copenha...
How Volvo embraced real-time 3D and shook up the auto industry- Unite Copenha...How Volvo embraced real-time 3D and shook up the auto industry- Unite Copenha...
How Volvo embraced real-time 3D and shook up the auto industry- Unite Copenha...Unity Technologies
 
QA your code: The new Unity Test Framework – Unite Copenhagen 2019
QA your code: The new Unity Test Framework – Unite Copenhagen 2019QA your code: The new Unity Test Framework – Unite Copenhagen 2019
QA your code: The new Unity Test Framework – Unite Copenhagen 2019Unity Technologies
 
Engineering.com webinar: Real-time 3D and digital twins: The power of a virtu...
Engineering.com webinar: Real-time 3D and digital twins: The power of a virtu...Engineering.com webinar: Real-time 3D and digital twins: The power of a virtu...
Engineering.com webinar: Real-time 3D and digital twins: The power of a virtu...Unity Technologies
 
Supplying scalable VR training applications with Innoactive - Unite Copenhage...
Supplying scalable VR training applications with Innoactive - Unite Copenhage...Supplying scalable VR training applications with Innoactive - Unite Copenhage...
Supplying scalable VR training applications with Innoactive - Unite Copenhage...Unity Technologies
 
XR and real-time 3D in automotive digital marketing strategies | Visionaries ...
XR and real-time 3D in automotive digital marketing strategies | Visionaries ...XR and real-time 3D in automotive digital marketing strategies | Visionaries ...
XR and real-time 3D in automotive digital marketing strategies | Visionaries ...Unity Technologies
 
Real-time CG animation in Unity: unpacking the Sherman project - Unite Copenh...
Real-time CG animation in Unity: unpacking the Sherman project - Unite Copenh...Real-time CG animation in Unity: unpacking the Sherman project - Unite Copenh...
Real-time CG animation in Unity: unpacking the Sherman project - Unite Copenh...Unity Technologies
 
Creating next-gen VR and MR experiences using Varjo VR-1 and XR-1 - Unite Cop...
Creating next-gen VR and MR experiences using Varjo VR-1 and XR-1 - Unite Cop...Creating next-gen VR and MR experiences using Varjo VR-1 and XR-1 - Unite Cop...
Creating next-gen VR and MR experiences using Varjo VR-1 and XR-1 - Unite Cop...Unity Technologies
 
What's ahead for film and animation with Unity 2020 - Unite Copenhagen 2019
What's ahead for film and animation with Unity 2020 - Unite Copenhagen 2019What's ahead for film and animation with Unity 2020 - Unite Copenhagen 2019
What's ahead for film and animation with Unity 2020 - Unite Copenhagen 2019Unity Technologies
 
How to Improve Visual Rendering Quality in VR - Unite Copenhagen 2019
How to Improve Visual Rendering Quality in VR - Unite Copenhagen 2019How to Improve Visual Rendering Quality in VR - Unite Copenhagen 2019
How to Improve Visual Rendering Quality in VR - Unite Copenhagen 2019Unity Technologies
 

Mehr von Unity Technologies (20)

Build Immersive Worlds in Virtual Reality
Build Immersive Worlds  in Virtual RealityBuild Immersive Worlds  in Virtual Reality
Build Immersive Worlds in Virtual Reality
 
Augmenting reality: Bring digital objects into the real world
Augmenting reality: Bring digital objects into the real worldAugmenting reality: Bring digital objects into the real world
Augmenting reality: Bring digital objects into the real world
 
Let’s get real: An introduction to AR, VR, MR, XR and more
Let’s get real: An introduction to AR, VR, MR, XR and moreLet’s get real: An introduction to AR, VR, MR, XR and more
Let’s get real: An introduction to AR, VR, MR, XR and more
 
Using synthetic data for computer vision model training
Using synthetic data for computer vision model trainingUsing synthetic data for computer vision model training
Using synthetic data for computer vision model training
 
The Tipping Point: How Virtual Experiences Are Transforming Global Industries
The Tipping Point: How Virtual Experiences Are Transforming Global IndustriesThe Tipping Point: How Virtual Experiences Are Transforming Global Industries
The Tipping Point: How Virtual Experiences Are Transforming Global Industries
 
Unity Roadmap 2020: Live games
Unity Roadmap 2020: Live games Unity Roadmap 2020: Live games
Unity Roadmap 2020: Live games
 
Unity Roadmap 2020: Core Engine & Creator Tools
Unity Roadmap 2020: Core Engine & Creator ToolsUnity Roadmap 2020: Core Engine & Creator Tools
Unity Roadmap 2020: Core Engine & Creator Tools
 
How ABB shapes the future of industry with Microsoft HoloLens and Unity - Uni...
How ABB shapes the future of industry with Microsoft HoloLens and Unity - Uni...How ABB shapes the future of industry with Microsoft HoloLens and Unity - Uni...
How ABB shapes the future of industry with Microsoft HoloLens and Unity - Uni...
 
Unity XR platform has a new architecture – Unite Copenhagen 2019
Unity XR platform has a new architecture – Unite Copenhagen 2019Unity XR platform has a new architecture – Unite Copenhagen 2019
Unity XR platform has a new architecture – Unite Copenhagen 2019
 
Turn Revit Models into real-time 3D experiences
Turn Revit Models into real-time 3D experiencesTurn Revit Models into real-time 3D experiences
Turn Revit Models into real-time 3D experiences
 
How Daimler uses mobile mixed realities for training and sales - Unite Copenh...
How Daimler uses mobile mixed realities for training and sales - Unite Copenh...How Daimler uses mobile mixed realities for training and sales - Unite Copenh...
How Daimler uses mobile mixed realities for training and sales - Unite Copenh...
 
How Volvo embraced real-time 3D and shook up the auto industry- Unite Copenha...
How Volvo embraced real-time 3D and shook up the auto industry- Unite Copenha...How Volvo embraced real-time 3D and shook up the auto industry- Unite Copenha...
How Volvo embraced real-time 3D and shook up the auto industry- Unite Copenha...
 
QA your code: The new Unity Test Framework – Unite Copenhagen 2019
QA your code: The new Unity Test Framework – Unite Copenhagen 2019QA your code: The new Unity Test Framework – Unite Copenhagen 2019
QA your code: The new Unity Test Framework – Unite Copenhagen 2019
 
Engineering.com webinar: Real-time 3D and digital twins: The power of a virtu...
Engineering.com webinar: Real-time 3D and digital twins: The power of a virtu...Engineering.com webinar: Real-time 3D and digital twins: The power of a virtu...
Engineering.com webinar: Real-time 3D and digital twins: The power of a virtu...
 
Supplying scalable VR training applications with Innoactive - Unite Copenhage...
Supplying scalable VR training applications with Innoactive - Unite Copenhage...Supplying scalable VR training applications with Innoactive - Unite Copenhage...
Supplying scalable VR training applications with Innoactive - Unite Copenhage...
 
XR and real-time 3D in automotive digital marketing strategies | Visionaries ...
XR and real-time 3D in automotive digital marketing strategies | Visionaries ...XR and real-time 3D in automotive digital marketing strategies | Visionaries ...
XR and real-time 3D in automotive digital marketing strategies | Visionaries ...
 
Real-time CG animation in Unity: unpacking the Sherman project - Unite Copenh...
Real-time CG animation in Unity: unpacking the Sherman project - Unite Copenh...Real-time CG animation in Unity: unpacking the Sherman project - Unite Copenh...
Real-time CG animation in Unity: unpacking the Sherman project - Unite Copenh...
 
Creating next-gen VR and MR experiences using Varjo VR-1 and XR-1 - Unite Cop...
Creating next-gen VR and MR experiences using Varjo VR-1 and XR-1 - Unite Cop...Creating next-gen VR and MR experiences using Varjo VR-1 and XR-1 - Unite Cop...
Creating next-gen VR and MR experiences using Varjo VR-1 and XR-1 - Unite Cop...
 
What's ahead for film and animation with Unity 2020 - Unite Copenhagen 2019
What's ahead for film and animation with Unity 2020 - Unite Copenhagen 2019What's ahead for film and animation with Unity 2020 - Unite Copenhagen 2019
What's ahead for film and animation with Unity 2020 - Unite Copenhagen 2019
 
How to Improve Visual Rendering Quality in VR - Unite Copenhagen 2019
How to Improve Visual Rendering Quality in VR - Unite Copenhagen 2019How to Improve Visual Rendering Quality in VR - Unite Copenhagen 2019
How to Improve Visual Rendering Quality in VR - Unite Copenhagen 2019
 

Kürzlich hochgeladen

Developer Data Modeling Mistakes: From Postgres to NoSQL
Developer Data Modeling Mistakes: From Postgres to NoSQLDeveloper Data Modeling Mistakes: From Postgres to NoSQL
Developer Data Modeling Mistakes: From Postgres to NoSQLScyllaDB
 
Beyond Boundaries: Leveraging No-Code Solutions for Industry Innovation
Beyond Boundaries: Leveraging No-Code Solutions for Industry InnovationBeyond Boundaries: Leveraging No-Code Solutions for Industry Innovation
Beyond Boundaries: Leveraging No-Code Solutions for Industry InnovationSafe Software
 
Human Factors of XR: Using Human Factors to Design XR Systems
Human Factors of XR: Using Human Factors to Design XR SystemsHuman Factors of XR: Using Human Factors to Design XR Systems
Human Factors of XR: Using Human Factors to Design XR SystemsMark Billinghurst
 
What's New in Teams Calling, Meetings and Devices March 2024
What's New in Teams Calling, Meetings and Devices March 2024What's New in Teams Calling, Meetings and Devices March 2024
What's New in Teams Calling, Meetings and Devices March 2024Stephanie Beckett
 
Unleash Your Potential - Namagunga Girls Coding Club
Unleash Your Potential - Namagunga Girls Coding ClubUnleash Your Potential - Namagunga Girls Coding Club
Unleash Your Potential - Namagunga Girls Coding ClubKalema Edgar
 
Search Engine Optimization SEO PDF for 2024.pdf
Search Engine Optimization SEO PDF for 2024.pdfSearch Engine Optimization SEO PDF for 2024.pdf
Search Engine Optimization SEO PDF for 2024.pdfRankYa
 
Commit 2024 - Secret Management made easy
Commit 2024 - Secret Management made easyCommit 2024 - Secret Management made easy
Commit 2024 - Secret Management made easyAlfredo García Lavilla
 
Designing IA for AI - Information Architecture Conference 2024
Designing IA for AI - Information Architecture Conference 2024Designing IA for AI - Information Architecture Conference 2024
Designing IA for AI - Information Architecture Conference 2024Enterprise Knowledge
 
"LLMs for Python Engineers: Advanced Data Analysis and Semantic Kernel",Oleks...
"LLMs for Python Engineers: Advanced Data Analysis and Semantic Kernel",Oleks..."LLMs for Python Engineers: Advanced Data Analysis and Semantic Kernel",Oleks...
"LLMs for Python Engineers: Advanced Data Analysis and Semantic Kernel",Oleks...Fwdays
 
The Future of Software Development - Devin AI Innovative Approach.pdf
The Future of Software Development - Devin AI Innovative Approach.pdfThe Future of Software Development - Devin AI Innovative Approach.pdf
The Future of Software Development - Devin AI Innovative Approach.pdfSeasiaInfotech2
 
Connect Wave/ connectwave Pitch Deck Presentation
Connect Wave/ connectwave Pitch Deck PresentationConnect Wave/ connectwave Pitch Deck Presentation
Connect Wave/ connectwave Pitch Deck PresentationSlibray Presentation
 
Ensuring Technical Readiness For Copilot in Microsoft 365
Ensuring Technical Readiness For Copilot in Microsoft 365Ensuring Technical Readiness For Copilot in Microsoft 365
Ensuring Technical Readiness For Copilot in Microsoft 3652toLead Limited
 
Unraveling Multimodality with Large Language Models.pdf
Unraveling Multimodality with Large Language Models.pdfUnraveling Multimodality with Large Language Models.pdf
Unraveling Multimodality with Large Language Models.pdfAlex Barbosa Coqueiro
 
Tampa BSides - Chef's Tour of Microsoft Security Adoption Framework (SAF)
Tampa BSides - Chef's Tour of Microsoft Security Adoption Framework (SAF)Tampa BSides - Chef's Tour of Microsoft Security Adoption Framework (SAF)
Tampa BSides - Chef's Tour of Microsoft Security Adoption Framework (SAF)Mark Simos
 
DevoxxFR 2024 Reproducible Builds with Apache Maven
DevoxxFR 2024 Reproducible Builds with Apache MavenDevoxxFR 2024 Reproducible Builds with Apache Maven
DevoxxFR 2024 Reproducible Builds with Apache MavenHervé Boutemy
 
"Subclassing and Composition – A Pythonic Tour of Trade-Offs", Hynek Schlawack
"Subclassing and Composition – A Pythonic Tour of Trade-Offs", Hynek Schlawack"Subclassing and Composition – A Pythonic Tour of Trade-Offs", Hynek Schlawack
"Subclassing and Composition – A Pythonic Tour of Trade-Offs", Hynek SchlawackFwdays
 
Streamlining Python Development: A Guide to a Modern Project Setup
Streamlining Python Development: A Guide to a Modern Project SetupStreamlining Python Development: A Guide to a Modern Project Setup
Streamlining Python Development: A Guide to a Modern Project SetupFlorian Wilhelm
 
"Debugging python applications inside k8s environment", Andrii Soldatenko
"Debugging python applications inside k8s environment", Andrii Soldatenko"Debugging python applications inside k8s environment", Andrii Soldatenko
"Debugging python applications inside k8s environment", Andrii SoldatenkoFwdays
 
Anypoint Exchange: It’s Not Just a Repo!
Anypoint Exchange: It’s Not Just a Repo!Anypoint Exchange: It’s Not Just a Repo!
Anypoint Exchange: It’s Not Just a Repo!Manik S Magar
 
My INSURER PTE LTD - Insurtech Innovation Award 2024
My INSURER PTE LTD - Insurtech Innovation Award 2024My INSURER PTE LTD - Insurtech Innovation Award 2024
My INSURER PTE LTD - Insurtech Innovation Award 2024The Digital Insurer
 

Kürzlich hochgeladen (20)

Developer Data Modeling Mistakes: From Postgres to NoSQL
Developer Data Modeling Mistakes: From Postgres to NoSQLDeveloper Data Modeling Mistakes: From Postgres to NoSQL
Developer Data Modeling Mistakes: From Postgres to NoSQL
 
Beyond Boundaries: Leveraging No-Code Solutions for Industry Innovation
Beyond Boundaries: Leveraging No-Code Solutions for Industry InnovationBeyond Boundaries: Leveraging No-Code Solutions for Industry Innovation
Beyond Boundaries: Leveraging No-Code Solutions for Industry Innovation
 
Human Factors of XR: Using Human Factors to Design XR Systems
Human Factors of XR: Using Human Factors to Design XR SystemsHuman Factors of XR: Using Human Factors to Design XR Systems
Human Factors of XR: Using Human Factors to Design XR Systems
 
What's New in Teams Calling, Meetings and Devices March 2024
What's New in Teams Calling, Meetings and Devices March 2024What's New in Teams Calling, Meetings and Devices March 2024
What's New in Teams Calling, Meetings and Devices March 2024
 
Unleash Your Potential - Namagunga Girls Coding Club
Unleash Your Potential - Namagunga Girls Coding ClubUnleash Your Potential - Namagunga Girls Coding Club
Unleash Your Potential - Namagunga Girls Coding Club
 
Search Engine Optimization SEO PDF for 2024.pdf
Search Engine Optimization SEO PDF for 2024.pdfSearch Engine Optimization SEO PDF for 2024.pdf
Search Engine Optimization SEO PDF for 2024.pdf
 
Commit 2024 - Secret Management made easy
Commit 2024 - Secret Management made easyCommit 2024 - Secret Management made easy
Commit 2024 - Secret Management made easy
 
Designing IA for AI - Information Architecture Conference 2024
Designing IA for AI - Information Architecture Conference 2024Designing IA for AI - Information Architecture Conference 2024
Designing IA for AI - Information Architecture Conference 2024
 
"LLMs for Python Engineers: Advanced Data Analysis and Semantic Kernel",Oleks...
"LLMs for Python Engineers: Advanced Data Analysis and Semantic Kernel",Oleks..."LLMs for Python Engineers: Advanced Data Analysis and Semantic Kernel",Oleks...
"LLMs for Python Engineers: Advanced Data Analysis and Semantic Kernel",Oleks...
 
The Future of Software Development - Devin AI Innovative Approach.pdf
The Future of Software Development - Devin AI Innovative Approach.pdfThe Future of Software Development - Devin AI Innovative Approach.pdf
The Future of Software Development - Devin AI Innovative Approach.pdf
 
Connect Wave/ connectwave Pitch Deck Presentation
Connect Wave/ connectwave Pitch Deck PresentationConnect Wave/ connectwave Pitch Deck Presentation
Connect Wave/ connectwave Pitch Deck Presentation
 
Ensuring Technical Readiness For Copilot in Microsoft 365
Ensuring Technical Readiness For Copilot in Microsoft 365Ensuring Technical Readiness For Copilot in Microsoft 365
Ensuring Technical Readiness For Copilot in Microsoft 365
 
Unraveling Multimodality with Large Language Models.pdf
Unraveling Multimodality with Large Language Models.pdfUnraveling Multimodality with Large Language Models.pdf
Unraveling Multimodality with Large Language Models.pdf
 
Tampa BSides - Chef's Tour of Microsoft Security Adoption Framework (SAF)
Tampa BSides - Chef's Tour of Microsoft Security Adoption Framework (SAF)Tampa BSides - Chef's Tour of Microsoft Security Adoption Framework (SAF)
Tampa BSides - Chef's Tour of Microsoft Security Adoption Framework (SAF)
 
DevoxxFR 2024 Reproducible Builds with Apache Maven
DevoxxFR 2024 Reproducible Builds with Apache MavenDevoxxFR 2024 Reproducible Builds with Apache Maven
DevoxxFR 2024 Reproducible Builds with Apache Maven
 
"Subclassing and Composition – A Pythonic Tour of Trade-Offs", Hynek Schlawack
"Subclassing and Composition – A Pythonic Tour of Trade-Offs", Hynek Schlawack"Subclassing and Composition – A Pythonic Tour of Trade-Offs", Hynek Schlawack
"Subclassing and Composition – A Pythonic Tour of Trade-Offs", Hynek Schlawack
 
Streamlining Python Development: A Guide to a Modern Project Setup
Streamlining Python Development: A Guide to a Modern Project SetupStreamlining Python Development: A Guide to a Modern Project Setup
Streamlining Python Development: A Guide to a Modern Project Setup
 
"Debugging python applications inside k8s environment", Andrii Soldatenko
"Debugging python applications inside k8s environment", Andrii Soldatenko"Debugging python applications inside k8s environment", Andrii Soldatenko
"Debugging python applications inside k8s environment", Andrii Soldatenko
 
Anypoint Exchange: It’s Not Just a Repo!
Anypoint Exchange: It’s Not Just a Repo!Anypoint Exchange: It’s Not Just a Repo!
Anypoint Exchange: It’s Not Just a Repo!
 
My INSURER PTE LTD - Insurtech Innovation Award 2024
My INSURER PTE LTD - Insurtech Innovation Award 2024My INSURER PTE LTD - Insurtech Innovation Award 2024
My INSURER PTE LTD - Insurtech Innovation Award 2024
 

Best practices: Async vs. coroutines - Unite Copenhagen 2019

  • 1.
  • 2. Async vs Coroutines: Best Practices Johannes Ahvenniemi Lead Programmer, Seriously Digital Entertainment
  • 3. About me 3 — Started with: BASIC — First published “indie” game: Java — First professional job: ActionScript 3 (Flash) — Favourite language: C++ — More tools in the box: C#, Clojure, Lua, JavaScript, OpenGL… — Unity developer since 2014 — Struggling with “callback hell”
  • 4.
  • 5. Coroutines 5 — Useful for spanning logic across multiple frames — Cooperative multitasking (Single threaded) — Clever utilisation of C# generators (IEnumerators) — This talk is about Unity’s Coroutines, not generators — Familiar from Go, Lua, Python and many other languages
  • 6. async/await 6 — Not intended for replacing Coroutines — Useful for handling non-blocking IO (and long lasting routines, i.e. threads) — Old C# feature (originating from F# (originating from Haskell)) — Now also in JavaScript, Python, Dart, C++, Rust… — Finally introduced in Unity 2017
  • 8. Coroutine Async Doesn’t have return values Has return values Can’t run synchronously Can run synchronously Doesn’t work without the engine Works without the engine Doesn’t support try/catch Supports try/catch Doesn’t preserve call stack Preserves call stack Always shows exceptions Can hide exceptions Doesn’t always exit Always exits Lifetime tied to a MonoBehaviour Lifetime handled manually Familiar to most Unity developers Unfamiliar to many Unity developers Coroutine Async Doesn’t have return values Has return values Can’t run synchronously Can run synchronously Doesn’t work without the engine Works without the engine Doesn’t support try/catch Supports try/catch Doesn’t preserve call stack Preserves call stack Always shows exceptions Can hide exceptions Doesn’t always exit Always exits Lifetime tied to a MonoBehaviour Lifetime handled manually Familiar to most Unity developers Unfamiliar to many Unity developers
  • 9. Coroutine Async Doesn’t have return values Has return values Can’t run synchronously Can run synchronously Doesn’t work without the engine Works without the engine Doesn’t support try/catch Supports try/catch Doesn’t preserve call stack Preserves call stack Always shows exceptions Can hide exceptions Doesn’t always exit Always exits Lifetime tied to a MonoBehaviour Lifetime handled manually Familiar to most Unity developers Unfamiliar to many Unity developers Coroutine Async Doesn’t have return values Has return values Can’t run synchronously Can run synchronously Doesn’t work without the engine Works without the engine Doesn’t support try/catch Supports try/catch Doesn’t preserve call stack Preserves call stack Always shows exceptions Can hide exceptions Doesn’t always exit Always exits Lifetime tied to a MonoBehaviour Lifetime handled manually Familiar to most Unity developers Unfamiliar to many Unity developers
  • 10. 10 Disclaimer: The following examples aren’t production quality. They are full of memory leaks and bad practices. Don’t just copy these examples into your product!
  • 11. Coroutine Doesn’t have return values IEnumerator Start() { IEnumerator enumerator = SelectButton(); yield return enumerator; Button selectedButton = (Button)enumerator.Current; Assert.IsTrue(selectedButton != null); Debug.Log(selectedButton.name); } static IEnumerator SelectButton() { Button selectedButton = null; Button[] buttons = FindObjectsOfType<Button>(); foreach (Button button in buttons) { Button b = button; b.onClick.AddListener(() => selectedButton = b); } while (selectedButton == null) { yield return null; } yield return selectedButton; } 11 Async Has return values async void Start() { Button selectedButton = await SelectButton(); Debug.Log(selectedButton.name); } static async Task<Button> SelectButton() { Button[] buttons = FindObjectsOfType<Button>(); var tasks = buttons.Select(PressButton); Task<Button> finishedTask = await Task.WhenAny(tasks); return finishedTask.Result; } static async Task<Button> PressButton(Button button) { bool isPressed = false; button.onClick.AddListener(() => isPressed = true); while (!isPressed) { await Task.Yield(); } return button; }
  • 12. Coroutine Async Doesn’t have return values Has return values Can’t run synchronously Can run synchronously Doesn’t work without the engine Works without the engine Doesn’t support try/catch Supports try/catch Doesn’t preserve call stack Preserves call stack Always shows exceptions Can hide exceptions Doesn’t always exit Always exits Lifetime tied to a MonoBehaviour Lifetime handled manually Familiar to most Unity developers Unfamiliar to many Unity developers Coroutine Async Doesn’t have return values Has return values Can’t run synchronously Can run synchronously Doesn’t work without the engine Works without the engine Doesn’t support try/catch Supports try/catch Doesn’t preserve call stack Preserves call stack Always shows exceptions Can hide exceptions Doesn’t always exit Always exits Lifetime tied to a MonoBehaviour Lifetime handled manually Familiar to most Unity developers Unfamiliar to many Unity developers
  • 13. Coroutine Can’t run synchronously IEnumerator Start() { Debug.Log($"Frame: {Time.frameCount}"); yield return Function(); Debug.Log($"Frame: {Time.frameCount}"); } static IEnumerator Function() { if (false) yield return null; } 13 Async Can run synchronously async void Start() { Debug.Log($"Frame: {Time.frameCount}"); await Function(); Debug.Log($"Frame: {Time.frameCount}"); } static async Task Function() { if (false) await Task.Yield(); } Outputs: Frame: 1 Frame: 2 Outputs: Frame: 1 Frame: 1
  • 14. Coroutine Async Doesn’t have return values Has return values Can’t run synchronously Can run synchronously Doesn’t work without the engine Works without the engine Doesn’t support try/catch Supports try/catch Doesn’t preserve call stack Preserves call stack Always shows exceptions Can hide exceptions Doesn’t always exit Always exits Lifetime tied to a MonoBehaviour Lifetime handled manually Familiar to most Unity developers Unfamiliar to many Unity developers Coroutine Async Doesn’t have return values Has return values Can’t run synchronously Can run synchronously Doesn’t work without the engine Works without the engine Doesn’t support try/catch Supports try/catch Doesn’t preserve call stack Preserves call stack Always shows exceptions Can hide exceptions Doesn’t always exit Always exits Lifetime tied to a MonoBehaviour Lifetime handled manually Familiar to most Unity developers Unfamiliar to many Unity developers
  • 15. 15
  • 16. Coroutine Async Doesn’t have return values Has return values Can’t run synchronously Can run synchronously Doesn’t work without the engine Works without the engine Doesn’t support try/catch Supports try/catch Doesn’t preserve call stack Preserves call stack Always shows exceptions Can hide exceptions Doesn’t always exit Always exits Lifetime tied to a MonoBehaviour Lifetime handled manually Familiar to most Unity developers Unfamiliar to many Unity developers Coroutine Async Doesn’t have return values Has return values Can’t run synchronously Can run synchronously Doesn’t work without the engine Works without the engine Doesn’t support try/catch Supports try/catch Doesn’t preserve call stack Preserves call stack Always shows exceptions Can hide exceptions Doesn’t always exit Always exits Lifetime tied to a MonoBehaviour Lifetime handled manually Familiar to most Unity developers Unfamiliar to many Unity developers
  • 17. Coroutine Doesn’t support try/catch public class Root : MonoBehaviour { IEnumerator Start() { try { yield return MainMenu(); } catch (Exception) { SceneManager.LoadScene("Error"); throw; } } } 17 Async Supports try/catch public class Root : MonoBehaviour { async void Start() { try { await MainMenu(); } catch (Exception) { SceneManager.LoadScene("Error"); throw; } } } Compiler error! Works!
  • 18. Coroutine Async Doesn’t have return values Has return values Can’t run synchronously Can run synchronously Doesn’t work without the engine Works without the engine Doesn’t support try/catch Supports try/catch Doesn’t preserve call stack Preserves call stack Always shows exceptions Can hide exceptions Doesn’t always exit Always exits Lifetime tied to a MonoBehaviour Lifetime handled manually Familiar to most Unity developers Unfamiliar to many Unity developers Coroutine Async Doesn’t have return values Has return values Can’t run synchronously Can run synchronously Doesn’t work without the engine Works without the engine Doesn’t support try/catch Supports try/catch Doesn’t preserve call stack Preserves call stack Always shows exceptions Can hide exceptions Doesn’t always exit Always exits Lifetime tied to a MonoBehaviour Lifetime handled manually Familiar to most Unity developers Unfamiliar to many Unity developers
  • 19. 19 Coroutine Doesn’t preserve call stack Async Preserves call stack
  • 20. 20 Coroutine Doesn’t preserve call stack Async Preserves call stack
  • 21. 21 Coroutine Doesn’t preserve call stack Async Preserves call stack
  • 22. 22 Async call stack in the profiler :(
  • 23. 23 Mind the stack! Watch out for asset references in the stack! UnloadUnusedAssets will unload the assets but might cause surprises My suggestion: Don’t await non-additive scenes from non-additive scenes
  • 24. Coroutine Async Doesn’t have return values Has return values Can’t run synchronously Can run synchronously Doesn’t work without the engine Works without the engine Doesn’t support try/catch Supports try/catch Doesn’t preserve call stack Preserves call stack Always shows exceptions Can hide exceptions Doesn’t always exit Always exits Lifetime tied to a MonoBehaviour Lifetime handled manually Familiar to most Unity developers Unfamiliar to many Unity developers Coroutine Async Doesn’t have return values Has return values Can’t run synchronously Can run synchronously Doesn’t work without the engine Works without the engine Doesn’t support try/catch Supports try/catch Doesn’t preserve call stack Preserves call stack Always shows exceptions Can hide exceptions Doesn’t always exit Always exits Lifetime tied to a MonoBehaviour Lifetime handled manually Familiar to most Unity developers Unfamiliar to many Unity developers
  • 25. Coroutine Always shows exceptions void Start() { this.StartCoroutine(Function()); } static IEnumerator Function() { yield return null; Debug.Log("About to throw exception"); throw new Exception(); } 25 Async Can hide exceptions void Start() { Function(); } static async Task Function() { await Task.Yield(); Debug.Log("About to throw exception"); throw new Exception(); }
  • 26. Coroutine Async Doesn’t have return values Has return values Can’t run synchronously Can run synchronously Doesn’t work without the engine Works without the engine Doesn’t support try/catch Supports try/catch Doesn’t preserve call stack Preserves call stack Always shows exceptions Can hide exceptions Doesn’t always exit Always exits Lifetime tied to a MonoBehaviour Lifetime handled manually Familiar to most Unity developers Unfamiliar to many Unity developers Coroutine Async Doesn’t have return values Has return values Can’t run synchronously Can run synchronously Doesn’t work without the engine Works without the engine Doesn’t support try/catch Supports try/catch Doesn’t preserve call stack Preserves call stack Always shows exceptions Can hide exceptions Doesn’t always exit Always exits Lifetime tied to a MonoBehaviour Lifetime handled manually Familiar to most Unity developers Unfamiliar to many Unity developers
  • 27. Coroutine Doesn’t always exit IEnumerator ShowEffect(RawImage container) { var texture = new RenderTexture(512, 512, 0); try { container.texture = texture; for (int i = 0; i < 100; ++i) { /* * Update effect */ yield return null; } } finally { texture.Release(); } } 27 Async Always exits async Task ShowEffect(RawImage container) { var texture = new RenderTexture(512, 512, 0); try { container.texture = texture; for (int i = 0; i < 100; ++i) { /* * Update effect */ await Task.Yield(); } } finally { texture.Release(); } } Leaks if owning MonoBehaviour is destroyed! Never leaks!
  • 28. Coroutine Async Doesn’t have return values Has return values Can’t run synchronously Can run synchronously Doesn’t work without the engine Works without the engine Doesn’t support try/catch Supports try/catch Doesn’t preserve call stack Preserves call stack Always shows exceptions Can hide exceptions Doesn’t always exit Always exits Lifetime tied to a MonoBehaviour Lifetime handled manually Familiar to most Unity developers Unfamiliar to many Unity developers Coroutine Async Doesn’t have return values Has return values Can’t run synchronously Can run synchronously Doesn’t work without the engine Works without the engine Doesn’t support try/catch Supports try/catch Doesn’t preserve call stack Preserves call stack Always shows exceptions Can hide exceptions Doesn’t always exit Always exits Lifetime tied to a MonoBehaviour Lifetime handled manually Familiar to most Unity developers Unfamiliar to many Unity developers
  • 29. Coroutine Lifetime tied to a MonoBehaviour IEnumerator Start() { this.StartCoroutine(this.Rotate()); yield return new WaitForSeconds(1.0f); Destroy(gameObject); } IEnumerator Rotate() { while (true) { transform.Rotate(Vector3.forward, 1.0f); yield return null; } } 29 Async Lifetime handled manually async void Start() { this.Rotate(); await Task.Delay(1000); Destroy(gameObject); } async void Rotate() { while (true) { transform.Rotate(Vector3.forward, 1.0f); await Task.Yield(); } } Throws NullRef Exception!Stops silently!
  • 30. 30 Async Lifetime handled manually Fixed? No. async void Start() { var cts = new CancellationTokenSource(); this.Rotate(cts.Token); await Task.Delay(1000); cts.Cancel(); Destroy(this.gameObject); } async void Rotate(CancellationToken ct) { while (!ct.IsCancellationRequested) { this.transform.Rotate(Vector3.forward, 1.0f); await Task.Yield(); } }
  • 31. Coroutine Async Doesn’t have return values Has return values Can’t run synchronously Can run synchronously Doesn’t work without the engine Works without the engine Doesn’t support try/catch Supports try/catch Doesn’t preserve call stack Preserves call stack Always shows exceptions Can hide exceptions Doesn’t always exit Always exits Lifetime tied to a MonoBehaviour Lifetime handled manually Familiar to most Unity developers Unfamiliar to many Unity developers Coroutine Async Doesn’t have return values Has return values Can’t run synchronously Can run synchronously Doesn’t work without the engine Works without the engine Doesn’t support try/catch Supports try/catch Doesn’t preserve call stack Preserves call stack Always shows exceptions Can hide exceptions Doesn’t always exit Always exits Lifetime tied to a MonoBehaviour Lifetime handled manually Familiar to most Unity developers Unfamiliar to many Unity developers
  • 33. 33
  • 34. public class PromptPopup1 : MonoBehaviour { public Button buttonAccept; public Button buttonCancel; public Button buttonClose; public static async Task<bool> Spawn() { PromptPopup1 p = await Utils.LoadScene<PromptPopup1>("Popup", LoadSceneMode.Additive); Button selected = await Utils.SelectButton(p.buttonAccept, p.buttonCancel, p.buttonClose); return selectedButton == p.buttonAccept; } } 34 Simple prompt popup
  • 35. public class PromptPopup2 : MonoBehaviour { public Button buttonAccept; public Button buttonCancel; public Button buttonClose; public static async Task<bool> Spawn() { PromptPopup2 p = await Utils.LoadScene<PromptPopup2>("Popup", LoadSceneMode.Additive); Task<Button> selectButtonTask = Utils.SelectButton(p.buttonAccept, p.buttonCancel, p.buttonClose); Task pressBackButtonTask = Utils.WaitForPressBackButton(); Task finishedTask = await Task.WhenAny(selectButtonTask, pressBackButtonTask); await finishedTask; // Propagate exception if the task finished because of exception return finishedTask == selectButtonTask && selectButtonTask.Result == p.buttonAccept; } } 35 Implement Android back button
  • 36. 36 Implement Android back button public class PromptPopup2 : MonoBehaviour { public Button buttonAccept; public Button buttonCancel; public Button buttonClose; public static async Task<bool> Spawn() { PromptPopup2 p = await Utils.LoadScene<PromptPopup2>("Popup", LoadSceneMode.Additive); Task<Button> selectButtonTask = Utils.SelectButton(p.buttonAccept, p.buttonCancel, p.buttonClose); Task pressBackButtonTask = Utils.WaitForPressBackButton(); Task finishedTask = await Task.WhenAny(selectButtonTask, pressBackButtonTask); await finishedTask; // Propagate exception if the task finished because of exception return finishedTask == selectButtonTask && selectButtonTask.Result == p.buttonAccept; } }
  • 37. public class PromptPopup3 : MonoBehaviour { public Button buttonAccept; public Button buttonCancel; public Button buttonClose; public static async Task<bool> Spawn(CancellationToken ct) { PromptPopup3 p = await Utils.LoadScene<PromptPopup3>("Popup", LoadSceneMode.Additive, ct); var selectButtonTask = Utils.SelectButton(ct, p.buttonAccept, p.buttonCancel, p.buttonClose); var pressBackButtonTask = Utils.WaitForPressBackButton(ct); var finishedTask = await Task.WhenAny(selectButtonTask, pressBackButtonTask); await finishedTask; // Propagate exception if the task finished because of exception return finishedTask == selectButtonTask && selectButtonTask.Result == p.buttonAccept; } } 37 Implement manual cancellation
  • 38. public class PromptPopup4 : MonoBehaviour { public Button buttonAccept; public Button buttonCancel; public Button buttonClose; public static async Task<bool> Spawn(CancellationToken ct) { PromptPopup4 p = await Utils.LoadScene<PromptPopup4>("Popup", LoadSceneMode.Additive, ct); var linkedCts = CancellationTokenSource.CreateLinkedTokenSource(ct); var linkedCt = linkedCts.Token; var buttonTask = Utils.SelectButton(linkedCt, p.buttonAccept, p.buttonCancel, p.buttonClose); var pressBackButtonTask = Utils.WaitForPressBackButton(linkedCt); var finishedTask = await Task.WhenAny(buttonTask, pressBackButtonTask); await finishedTask; // Propagate exception if the task finished because of exception linkedCts.Cancel(); return finishedTask == buttonTask && buttonTask.Result == p.buttonAccept; } } 38 Clean up remaining tasks
  • 39. public class PromptPopup5 : MonoBehaviour { public Button buttonAccept; public Button buttonCancel; public Button buttonClose; public static async Task<bool> Spawn(CancellationToken ct) { PromptPopup5 p = await Utils.LoadScene<PromptPopup5>("Popup", LoadSceneMode.Additive, ct); using (var linkedCts = CancellationTokenSource.CreateLinkedTokenSource(ct)) { var linkedCt = linkedCts.Token; var buttonTask = Utils.SelectButton(linkedCt, p.buttonAccept, p.buttonCancel, p.buttonClose); var pressBackButtonTask = Utils.WaitForPressBackButton(linkedCt); var finishedTask = await Task.WhenAny(buttonTask, pressBackButtonTask); await finishedTask; // Propagate exception if the task finished because of exception linkedCts.Cancel(); return finishedTask == buttonTask && buttonTask.Result == p.buttonAccept; } } } 39 Dispose linked CancellationTokenSource
  • 40. public static async Task<bool> Spawn(CancellationToken ct) { PromptPopup6 p = await Utils.LoadScene<PromptPopup6>("Popup", LoadSceneMode.Additive, ct); var linkedCts = CancellationTokenSource.CreateLinkedTokenSource(ct); try { var linkedCt = linkedCts.Token; var buttonTask = Utils.SelectButton(linkedCt, p.buttonAccept, p.buttonCancel, p.buttonClose); var pressBackButtonTask = Utils.WaitForPressBackButton(linkedCt); var finishedTask = await Task.WhenAny(buttonTask, pressBackButtonTask); await finishedTask; // Propagate exception if the task finished because of exception linkedCts.Cancel(); return finishedTask == buttonTask && buttonTask.Result == popup.buttonAccept; } finally { linkedCts.Dispose(); popup.StartCoroutine(Utils.PlayPopupCloseAnimation(p.gameObject)); } } 40 Implement closing the popup
  • 41. public static async Task<bool> Spawn(CancellationToken ct) { PromptPopup6 p = await Utils.LoadScene<PromptPopup6>("Popup", LoadSceneMode.Additive, ct); var linkedCts = CancellationTokenSource.CreateLinkedTokenSource(ct); try { var linkedCt = linkedCts.Token; var buttonTask = Utils.SelectButton(linkedCt, p.buttonAccept, p.buttonCancel, p.buttonClose); var pressBackButtonTask = Utils.WaitForPressBackButton(linkedCt); var finishedTask = await Task.WhenAny(buttonTask, pressBackButtonTask); await finishedTask; // Propagate exception if the task finished because of exception linkedCts.Cancel(); return finishedTask == buttonTask && buttonTask.Result == popup.buttonAccept; } finally { linkedCts.Dispose(); popup.StartCoroutine(Utils.PlayPopupCloseAnimation(p.gameObject)); } } 41 Implement closing the popup
  • 42. 42
  • 43. Choosing between Async and Coroutines 43
  • 44. Choosing between Async and Coroutines Rules of Thumb 44 1. Use async when you deal with IO (User input, Network calls) 2. Use coroutines for fire-and-forget routines. 3. Don’t intertwine them
  • 45. Thank you! Johannes Ahvenniemi Lead Programmer, Seriously Digital Entertainment johannes@seriously.com
  • 46. We are hiring! Johannes Ahvenniemi Lead Programmer, Seriously Digital Entertainment johannes@seriously.com

Hinweis der Redaktion

  1. Hello! And this talk is async vs coroutines, best practices. My name Johannes Ahvenniemi, I’m from Seriously Digital Entertainment Let’s start by asking who here is a coder? I hope most of you because this is a very code focused talk.
  2. I’ll start with telling about me, so you can better relate to what I’m saying. A basis for how I ended up talking about this subject. I started quite young, my first language was a BASIC, for Amstrad. We did a PC, but Amstrad was more approachable. You’d just type in commands and it would print and draw stuff for you. I did move on to QBasic on PC at some point. Who here has used QBasic? That was a lot of fun, right? You would just write this sequential code and do amazing stuff with it. Coding used to be so simple! I did some C++ in between but when Java came I was able to make easily distributable browser games, so that’s how I published my first game. Something like ten people played it, which was cool. Who where has made a game in Java? It’s not really great for games, is it… Then I realised how many players I’d get if I downgraded to flash! That’s how I saw it, as a downgrade. So I did that, and I’m happy I did because I achieved some mild success with my Flash games. Flash is also how I landed my first actual job in the industry, a flash developer at Rovio. Working on browser versions of Angry Birds. I also did some C++ and Lua at Rovio, and I use mostly C++ in my personal projects. It remains my favourite language for game dev. It’s a great language as long as you don’t go crazy with all the features available. Other technologies in my toolbox… A bunch of mostly web and game dev related stuff, and I’m also a fan of functional programming, so I use Clojure when I want to expand my mind. In case you didn’t know, functional programming is also good for your soul. - I started using Unity in 2014 when I joined Seriously, started working on the hit puzzle game Best Fiends. I hope some of you have heard of it. Best Fiends is a F2p so I’ve done lots of UI work. There’s lots of UI work that goes to F2P games, with all the sale and event popups and so on. And that’s fun but challenging work. Because these popups use lots of buttons, And buttons are handled with callbacks, And when you start chaining these callbacks you end up in what web developers call callback hell. So callback hell is a thing also in Unity, not only JavaScript. In JavaScript however we recently got rid of callbacks thanks to async. With async I could write my code sequentially once again, just like back in the days with QBasic. So I’m really happy for having async finally in Unity So we finally got to the topic
  3. , async! And Coroutines too of course But mainly async because it’s the new and exciting technology Actually, this talk is more async, but not only because it’s new and exciting but because it’s new and scary. But it’s also about coroutines because it can be really difficult to understand when to use which, async or coroutines.
  4. Let’s start with the good old coroutines. Who here has used them before? So you know coroutines are good for spanning logic across time. That’s pretty much what Unity documentation also states, I just reworded it to fit on one line. They are single threaded, which is important because the engine features are accessible only from the main thread. Under the hood they are actually just C# generators. So they aren’t exactly a language feature, but there is no magic under the hood. And to clarify, I’m focusing on Coroutines as an engine feature. When I’m saying that you can or can’t do something with coroutines, the same might not apply to generators. It’s also not a new concept. It’s available in many languages, I just listed a few here. So it’s clearly a useful.
  5. How about async/await. That’s right. We’re actually talking about two concepts here, async AND await, but I’ll just refer to this duality as simply async from now on. When async came to Unity, there was lots of discussion online about how you can now replace coroutines with async. I’m going to argue against that. I’m arguing that async is not intended for replacing coroutines. They can be very similar though. In some other language it can be hard to tell them apart. For example Clojure borrowed Goroutines from Go and called it async. Go figure. So what is async for then According to documentation, async is useful for handling non-blocking input. And threading, but I’m not talking about threads here today, for better focus. Before async these things were handled using callbacks. So async isn’t intended for replacing coroutines, async is pretty much a better way of handling callbacks. Async originates from functional languages. By now it’s spread from C# to many popular languages, so I would say it’s a very successful feature. We shouldn’t be afraid of using it. Finally we got it in Unity 2017, and now we are here wondering what to do with them.
  6. So let’s get started with the comparison between async and coroutines. This turned out to be quite a list, so hang tight.
  7. Alright, here’s the list. Coroutines on the left side, async on the right side. Now, some of the don’t seem that relevant, like the 2nd one, running async synchronously. And some of these are quite weird, like the 7th one, coroutines not always exiting. What’s up with that? That’s why I will go through all of these, with code examples, so we can understand them better.
  8. First one. Coroutines don’t have return values. Async does. This is the most important one. To put it simply, it’s the true reason to pick async over coroutines. The rest are just details If you need an asynchronous operation to return a value, you use async. That’s it. Our first code example shows how this works.
  9. But before that, a brief disclaimer: These examples are terrible. You don’t want to use them. They are designed to present ideas, they are not meant to be used as is. So please, don’t. Okay? Let’s do it.
  10. In this example we wait for a button press and log the name of the button. The async version’s Start method describes this well. Wait for select button, log name. Doesn’t get simpler than that, right? The coroutine on the other hand… So I lied. You CAN have return values with coroutines, like in this example, but not if you value your pride as a clean coder. I’m not even sure it works. It seemed to work! But with anything more complex, I wouldn’t dare to try this. The bottom line is: coroutines don’t have return values! Async on the other hand does. And it looks amazing! It almost feels like it was made for this. And it was! Async was created for handling asynchronous input. A button press is exactly that. It happens asynchronously at some point. The callback is wrapped in the PressButton method. We yield until it happens. And then in SelectButton we await for the first button press task to finish, and we return the pressed button. There’s also a Linq operation there, can you see it? If you can use Linq to handle your input, you must be doing something right. Also Linq is good for your soul, in case you didn’t know. Understanding what goes on here is vital to understand the benefits of async. The point of this example is to convince the non-believers that async does have a use case where coroutines don’t really cut it. Now are you convinced? Good.
  11. Next one! A less exciting one. Quite a weird one. Why would you even want to run async synchronously? Well, our games are asynchronous, right? Things happen over time, frame by frame, picking up user input along the way. But what if we want to run the game in batch mode? We’d just give a list of inputs as parameter and we’d get out the end state. Let’s say it’s for testing purposes, or maybe we’re validating a high score some user got. Now, this should be a synchronous operation. Give input, get result. There is nothing we need to “wait” there. Well, turns out this is tricky with coroutines. With async it’s at least less tricky. Check this out:
  12. Look at the outputs. Why did we lose one frame there? Even with the if (false) there, it still ends up losing one frame somewhere. It’s not synchronous! And that’s annoying! With async, we don't have this issue. With async we know we at least CAN run our logic synchronously if the need be. And that’s great.
  13. The third one is related to the previous one. If we want to run the game in the cloud, for score validation for example, and if our logic uses coroutines, we need to put the whole Unity engine in the cloud. If we don’t use engine features we can run it in headless mode, which is much simpler and faster I don’t have a code example of this so I’m letting Drake explain it to you:
  14. You can’t have coroutines without the engine. Async you can. Of course if your logic needs the engine for stuff like physics, there’s no way around it. But for something like a matching game, it doesn’t NEED the engine for anything, and we can theoretically run it without Unity. Makes it much easier to put it in the cloud.
  15. Coroutines don’t support try/catch. Async does. That’s pretty straight forward. A very powerful feature in async.
  16. Now we can finally do this. And this is a big one. It’s not only about try catch. With async we can build our whole game as sequential chain of logic. Everything starting from a Root, which is waiting the main menu, which is maybe waiting for a popup, which is waiting for a button press, and so on. There are many benefits to this approach, and one is better error handling. When an uncaught exception occurs in our logic, when everything is lost, we can just load an error scene saying that we’re sorry, please restart the game. Or even better, put this thing in a while loop and give a restart button. With Coroutines we simply can’t do this kind of things. They aren’t designed for this. They don’t even compile if you try to do this.
  17. This is an interesting one. Who thinks preserving the call stack is a positive thing? Hands up if you think so. Who thinks it’s a bad thing? Hands up. The rest of you either know it’s a trick question… or you spaced out. I don’t blame you. Turns out both have pros and cons. And now, I must point out that I’m spilling some lies here. It’s not exactly this simple. But it’s a pragmatic way of looking at this, so I’m going with it.
  18. Here is the logging of a NullReferenceException originating from somewhere The coroutine one is less intimidating. Not obvious, but it’s ok. But it’s also less informative. We see the exception is coming from DoStuff, but we have no idea how we ended up there! As you know, debugging exceptions is much easier when you see the call stack telling you how you ended up there. Where did that null value originate from. But Coroutines don’t tell you the stack. You’ve noticed this right? It’s annoying! Async does give you the call stack. It’s a bit scary looking, but it’s there. Look here: DoStuff, DoSomething, Start. Now you have something to work with. You’ll get used to the messy output.
  19. Here is the logging of a NullReferenceException originating from somewhere The coroutine one is less intimidating. Not obvious, but it’s ok. But it’s also less informative. We see the exception is coming from DoStuff, but we have no idea how we ended up there! As you know, debugging exceptions is much easier when you see the call stack telling you how you ended up there. Where did that null value originate from. But Coroutines don’t tell you the stack. You’ve noticed this right? It’s annoying!
  20. Async does give you the call stack. It’s a bit scary looking, but it’s there. Look here: DoStuff called by DoSomething called by Start. Now you have something to work with. The output is just a bit messy, but you’ll get used to it.
  21. THIS however you don’t get used to. It’s extremely annoying. This is what the async stack looks like in the profiler. Look at the size of that! And this is only a fraction of the whole thing! It’s all I could fit on my 4k monitor! It’s really quite a mess. I hope Unity does something about this… You really don’t want to be clicking through that to reach the leaf nodes. Using the keyboard helps. Only recently I found the fastest way to get to the leaf nodes: Adjust your key repeating interval on your OS to the minimum, and hold down the right button. Before I figured that I lost some brain cells pressing “down” “right” “down” “right”. Jokes aside
  22. This is an actual warning. Unity doesn’t automatically unload assets referenced in your execution stack. Remember how I advertised how you can turn your app into one sequential flow? Root awaiting the main menu awaiting game scene, game scene awaiting the win scene, win scene awaiting the gacha scene and so on. But now you risk referencing assets in all these scenes in your execution stack. Unity won’t unload these assets automatically. This rarely is a problem with coroutines. It can happen with Coroutines too, but it doesn’t tend to be a problem due to how they are normally used. You could use UnloadUnusedAssets. That will clear assets despite references in the call stack. You could get away by doing this. But then again somewhere you might depend on the assets not being unloaded, so I can’t recommend doing that. Instead I recommend not going crazy with stacking async calls. Especially don’t await non-additive scenes from other non-additive scenes. Return early and let the root decide where to go next. Never go crazy with new tools…
  23. Now a small win for Coroutines! How come async doesn’t always show exceptions? Well, look a this
  24. The implementations are equal, but the log doesn’t show the exception in the async version. The Debug Log is printed but no-one is noticing and logging the exception! It’s an uncaught exception no one catches. What’s that, an un-uncaught exception? - Now, this is a stupid example. You would never write code like this. You would never return a Task and not await it. But the point here is that weird stuff happens if you don’t know what you’re doing. This example is supposed to scare you a bit. To think twice before choosing async over coroutines just because it’s new and shiny. You will end up fighting a completely new set of problems. So as with any new tool, you need to be mindful of what you’re doing.
  25. This is a surprising win for async. When a MonoBehaviour owning a Coroutine dies, the coroutine isn’t actually STOPPED. It’s just never resumed again. Let’s see how this can be a problem
  26. We have this kind of code in our project at the moment. It’s a bit scary actually. Here’s what we do: We allocate a render texture and assign it to a UI component. For 100 frames we apply filters and stuff to the render texture, creating a cool animated effect. Think of Final Fantasy 7’s effect for transition from world map to battle. Once done we have to release the texture. This is important because Unity won’t release it automatically. But what if the MonoBehaviour running the coroutine dies? The coroutine will never be resumed again. It will never reach the finally block! This is crazy! What’s the point of finally if it doesn’t always happen!? Async on the other hand is fine. You can’t really mess it up. So when ever you see a finally or using block in a Coroutine, watch out!
  27. So the lifetime of a coroutine is tied to a monobehaviour. This is a big win for Coroutines. This can be extremely convenient. Without this feature I think Coroutines might not be worth it. Without this, the talk could as well been titled ten reasons to ditch Coroutines, or how I learned to love async. So this feature really justifies the existence of Coroutines. Let’s see an example of how this can be useful
  28. Here we have a component that rotates the game object seemingly forever, but the game object is destroyed after a second With Coroutines, as the owning MonoBehaviour dies, the coroutine silently stops But async… Well NOW async decides it’s a good time to explain something went wrong. That we are trying to access the transform of a destroyed game object. But we don’t really want that exception, do we? It’s an exception we don’t want our QA to come asking us about. We just want it to die silently. And null checks isn’t an answer. Then what do we do?
  29. We handle it manually with CancellationTokens! That’s the async way. Does this fix the problem? No it doesn’t. What if some other script destroys the game object before the timer runs out? Then we have the same problem. So do we cancel it in OnDestroy? Maybe. But now we’re starting to add complexity that we don’t need! We can just use Coroutines! So it seems like Coroutines still have a place in our world: Coroutines are great for this kind of fire-and-forget routines. Routines that never return anything, routines that can silently die away when not needed, routines that are just for visuals. Next one.
  30. Last one. More of a psychological one. Coroutines are familiar to us. We’ve already used them a bunch, for years, and done beautiful things with them. There is no reason replace them with a new tool, with all new quirks and pitfalls, if there isn’t a good reason. By now you hopefully already learned that Async is different from Coroutines. It’s a new set of issues you need to be mindful of. If you still think it’s a good idea to just throw out your coroutines and replace them with async, stay put.
  31. In this next section we’re going to build a prompt popup using async. This example will both show the power and the responsibility that comes with async.
  32. We are creating something like this. An additive scene with a couple of buttons for accepting or declining something. And a close button. That’s simple, right?
  33. Ha, and it WAS simple. Here we have a MonoBehaviour with three buttons. We use our utility SelectButton from the previous section to wait for the user to pick one. Finally we return true or false, depending on if the user accepted the GDPR or not. We’re also using a tiny utility function for loading the scene and finding the MonoBehaviour in that scene. Just to keep our example short and nice. Also you can see I’m using terrible naming convention, calling the popup instance p, just so I can fit everything horizontally on one line. Was that it? No it wasn’t. That’s why the behaviour is called PromptPopup1. It means there will be a 2 and a 3 at least! So let’s get cracking! What’s missing?
  34. We forgot the back button. We’re making a mobile game, we need to support the android back button. No problem, let’s add it. We have a Utility function for it, really simple. Now, async is fantastic when it comes to handling multiple simultaneous tasks. Here we wait for either one of the UI buttons to be pressed, or for the Android back button to be pressed. WhenAny returns the task that finishes first. If the finished task is the button task, and the accept button was pressed, we return true. Back button means “no” What about that weird await in the middle?
  35. That one. The comment explains it. But still, what? The thing is, if a task finishes because of an exception, WhenAny won’t tell it to you straight. It will just tell you something finished. But of course we don’t want to continue if there was an exception. We have no idea what state our application is in. So we need to dig out the truth ourselves! This is a neat way of doing that. If the task was finished because of an exception, awaiting it again rethrows that exception. Otherwise it does nothing. Just a bit of boiler plate, we can live with this. What else did we forget? We forgot manual lifetime management.
  36. We forgot cancellation tokens. As we learned earlier, if we don’t manage the lifetime of async manually, we quickly start seeing unwanted exceptions in the log, and unexpected stuff starts happening. In general, every async function should take a cancellation token. In the current code base I work with, we don’t have a single async function that doesn’t take it, except for one async void Start in the root of the logic, where it all starts. It’s our way of staying on top of the game. You want to have control over async, and cancellation tokens are a must for that! They are the spine of your async solution! So let’s add them, and it’s not that bad. Now, we still missed one aspect of manual lifetime management We don’t want the tasks that didn’t yet finish remain running in the background with all their references to buttons and what not. We want to cancel them. WhenAny doesn’t do that for us. We need to do that manually too.
  37. We can do this using a linked cancellation token. We link the original cancellation token with a new one And now after the first task has finished we are able to cancel the remaining ones That makes sense, right? Now we’re almost done, but we just introduced a surprising memory leak.
  38. We need to dispose the linked cancellation token source. Normally cancellation token sources don’t need disposal. They implement IDisposable, but it’s not needed. But when creating a linked one, they need disposal, or they will never be collected by the GC. No way around it. So we put it inside a using block. Finally! Code is done and you try it out. And immediately notice we forgot something. We never implemented closing the popup visually! The logic exits, but nothing unloads the scene. And we want to play a closing animation before we unload the scene. So you need a piece of logic that plays a close animation and then unloads the popup. Sounds like an asynchronous routine, right? Doing things over time. But this time it’s purely a visual thing. The input logic doesn’t need to wait for the animation. We don’t care when the animation and unloading finishes, as long as it does. It’s a fire-and-forget routine. And actually with Coroutines doing this is extremely simple
  39. We convert our using block into a try-finally block, it was just syntactic sugar for this anyways. Now, what ever happens, who ever cancels what, despite any exception, we don’t care. When the logic of the popup exits, we just fire and forget a routine that animates and unloads the popup. And that’s it. That’s the end result.
  40. It took quite a few steps to get here. So was it worth it? Well, this is actually a very powerful pattern. The thing we did here isn’t exactly trivial. We avoided many common popup bugs without even noticing. For example, how many of you have had to fix bugs related to double tapping a button? You double tap quickly and now you have two popups open or something. Well, it isn’t a problem anymore. Only the first tap is registerd. The second one does nothing. How about tapping two buttons at once, that’s even worse! It will definitely take you to an undefined state. But again, here the problem just went away. Only the first one is registered.
  41. Or, how many of you had to fix bugs related to pressing buttons behind a popup? Like pressing that play or settings button there behind the popup? If you fail to block those inputs it will most likely put your app in some undefined broken state. But if what ever loaded this popup is built with the same pattern we just used, there should no-one awaiting those buttons to be pressed. There should only be a piece of logic awaiting for this popup to return. The benefits of this pattern are many and would be worth another talk so I’m going to leave this here. The point of this exercise was to show not only that async can be really useful, but also that it comes with much responsibility. Coroutines come with less responsibility. I would say async is much harder to use than coroutines. You don’t want to be using async for things that you CAN solve with easy coroutines.
  42. So in the end, how do we know when to use what? It’s time for our final slide.
  43. Here I called them rules of thumb, as in not strict rules. In my current project these are actually rules. Currently we never break them. And it’s not because we like rules, but because we don’t have any problems when we follow them. But I don’t know your projects. I don’t know your special needs. Maybe these rules don’t apply to you strictly. But they should still give some guidance. We didn’t have this list when we started the project, but through trial and error we developed an intuition for picking the right one. And slowly we were able to formalise it. So this list came from a practical approach. But it ended up matching what documentation states. Should have started with the documentation! As stated earlier, according to Microsoft’s documentation, besides threading, async is about handling user input and network calls. And that’s what we’ve mostly done today. We’ve used async for handling input. What about Coroutines. According to Unity documentation they are for good spanning logic across multiple frames. But async can do that too, so that’s not enough. We need to specify the use case a bit better. In our project we ended up with this: We use Coroutines for all fire-and-forget routines. You could use async for them too, but there is so much responsibility that come with it. And because fire-and-forget routines don’t return anything you don’t NEED async. So when we don’t need async, we go with easy and familiar coroutines. Finally, you’ll end up with stuff like Tweens in-between inputs, and fire-and-forget tweens. So should a, say, position tween be async or a coroutine? In my current project it can be either. We have duplicated functions TweenPosition to work with coroutines and async. The point of this is to avoid intertwining the two. Duplicating implementations isn’t optimal of course, but it hasn’t really been a problem for us. Notice that it is still OK to fire a fire-and-forget coroutine from async, as long as it doesn’t later return to the async flow, intertwining the two. Branching is good, but don’t intertwine them. - And that’s it! It’s this simple. And here ends my talk.
  44. his was my talk about async vs coroutines. Thank you for listening. I hope you learned something
  45. And of course: And we are hiring! Any questions?