理解C#中的 async await

2021-02-19 DotNet開發跳槽

一個老掉牙的話題,園子裡的相關優秀文章已經有很多了,我寫這篇文章完全是想以自己的思維方式來談一談自己的理解。(PS:文中涉及到了大量反編譯源碼,需要靜下心來細細品味)

1|0從簡單開始

為了更容易理解這個問題,我們舉一個簡單的例子:用異步的方式在控制臺上分兩步輸出「Hello World!」,我這邊使用的是Framework 4.5.2

class Program
{
static async Task Main(string[] args)
{
Console.WriteLine("Let's Go!");

await TestAsync();

Console.Write(" World!");
}

static Task TestAsync()
{
return Task.Run(() =>
{
Console.Write("Hello");
});
}
}

2|0探究反編譯後的源碼

接下來我們使用 .NET reflector (也可使用 dnSpy 等) 反編譯一下程序集,然後一步一步來探究 async await 內部的奧秘。

2|1Main方法

[DebuggerStepThrough]
private static void <Main>(string[] args)
{
Main(args).GetAwaiter().GetResult();
}

[AsyncStateMachine(typeof(<Main>d__0)), DebuggerStepThrough]
private static Task Main(string[] args)
{
<Main>d__0 stateMachine = new <Main>d__0
{
<>t__builder = AsyncTaskMethodBuilder.Create(),
args = args,
<>1__state = -1
};
stateMachine.<>t__builder.Start<<Main>d__0>(ref stateMachine);
return stateMachine.<>t__builder.Task;
}

// 實現了 IAsyncStateMachine 接口
[CompilerGenerated]
private sealed class <Main>d__0 : IAsyncStateMachine
{
// Fields
public int <>1__state;
public AsyncTaskMethodBuilder <>t__builder;
public string[] args;
private TaskAwaiter <>u__1;

// Methods
private void MoveNext() { }
[DebuggerHidden]
private void SetStateMachine(IAsyncStateMachine stateMachine) { }
}

臥槽!竟然有兩個 Main 方法:一個同步、一個異步。原來,雖然我們寫代碼時為了在 Main 方法中方便異步等待,將 void Main 改寫成了async Task Main,但是實際上程序入口仍是我們熟悉的那個 void Main。

另外,我們可以看到異步 Main 方法被標註了AsyncStateMachine特性,這是因為在我們的原始碼中,該方法帶有修飾符async,表示該方法是一個異步方法。

好,我們先看一下異步Main方法內部實現,它主要做了三件事:

首先,創建了一個類型為<Main>d__0的狀態機 stateMachine,並初始化了公共變量 <>t__builder、args、<>1__state = -1

然後,調用Start方法,藉助 stateMachine, 來執行我們在異步 Main 方法中寫的代碼

最後,將指示異步 Main 方法運行狀態的Task對象返回出去

2|2Start

首先,我們先來看一下Start的內部實現

// 所屬結構體:AsyncTaskMethodBuilder

[SecuritySafeCritical, DebuggerStepThrough, __DynamicallyInvokable]
public void Start<TStateMachine>(ref TStateMachine stateMachine) where TStateMachine: IAsyncStateMachine
{
if (((TStateMachine) stateMachine) == null)
{
throw new ArgumentNullException("stateMachine");
}
ExecutionContextSwitcher ecsw = new ExecutionContextSwitcher();
RuntimeHelpers.PrepareConstrainedRegions();
try
{
ExecutionContext.EstablishCopyOnWriteScope(ref ecsw);
// 狀態機狀態流轉
stateMachine.MoveNext();
}
finally
{
ecsw.Undo();
}
}

我猜,你只能看懂stateMachine.MoveNext(),對不對?好,那我們就來看看這個狀態機類<Main>d__0,並且著重看它的方法MoveNext。

2|3MoveNext

[CompilerGenerated]
private sealed class <Main>d__0 : IAsyncStateMachine
{
// Fields
public int <>1__state;
public AsyncTaskMethodBuilder <>t__builder;
public string[] args;
private TaskAwaiter <>u__1;

// Methods
private void MoveNext()
{
// 在 Main 方法中,我們初始化 <>1__state = -1,所以此時 num = -1
int num = this.<>1__state;
try
{
TaskAwaiter awaiter;
if (num != 0)
{
Console.WriteLine("Let's Go!");
// 調用 TestAsync(),獲取 awaiter,用於後續監控 TestAsync() 運行狀態
awaiter = Program.TestAsync().GetAwaiter();

// 一般來說,異步任務不會很快就完成,所以大多數情況下都會進入該分支
if (!awaiter.IsCompleted)
{
// 狀態機狀態從 -1 流轉為 0
this.<>1__state = num = 0;
this.<>u__1 = awaiter;
Program.<Main>d__0 stateMachine = this;
// 配置 TestAsync() 完成後的延續
this.<>t__builder.AwaitUnsafeOnCompleted<TaskAwaiter, Program.<Main>d__0>(ref awaiter, ref stateMachine);
return;
}
}
else
{
awaiter = this.<>u__1;
this.<>u__1 = new TaskAwaiter();
this.<>1__state = num = -1;
}
awaiter.GetResult();
Console.Write(" World!");
}
catch (Exception exception)
{
this.<>1__state = -2;
this.<>t__builder.SetException(exception);
return;
}
this.<>1__state = -2;
this.<>t__builder.SetResult();
}

[DebuggerHidden]
private void SetStateMachine(IAsyncStateMachine stateMachine)
{
}
}

先簡單理一下內部邏輯:

設置變量 num = -1,此時 num != 0,則會進入第一個if語句,

首先,執行Console.WriteLine("Let's Go!")

然後,調用異步方法TestAsync,TestAsync方法會在另一個線程池線程中執行,並獲取指示該方法運行狀態的 awaiter

如果此時TestAsync方法已執行完畢,則像沒有異步一般:

繼續執行接下來的Console.Write(" World!")

最後設置 <>1__state = -2,並設置異步 Main 方法的返回結果

如果此時TestAsync方法未執行完畢,則:

設置 <>1__state = num = 0

調用AwaitUnsafeOnCompleted方法,用於配置當TestAsync方法完成時的延續,即Console.Write(" World!")

返回指示異步 Main 方法執行狀態的 Task 對象,由於同步 Main 方法中通過使用GetResult()同步阻塞主線程等待任務結束,所以不會釋放主線程(廢話,如果釋放了程序就退出了)。不過對於其他子線程,一般會釋放該線程

大部分邏輯我們都可以很容易的理解,唯一需要深入研究的就是AwaitUnsafeOnCompleted,那我們接下來就看看它的內部實現

2|4AwaitUnsafeOnCompleted

// 所屬結構體:AsyncTaskMethodBuilder

[__DynamicallyInvokable]
public void AwaitUnsafeOnCompleted<TAwaiter, TStateMachine>(ref TAwaiter awaiter, ref TStateMachine stateMachine) where TAwaiter: ICriticalNotifyCompletion where TStateMachine: IAsyncStateMachine
{
this.m_builder.AwaitUnsafeOnCompleted<TAwaiter, TStateMachine>(ref awaiter, ref stateMachine);
}

// 所屬結構體:AsyncTaskMethodBuilder<TResult>

[SecuritySafeCritical, __DynamicallyInvokable]
public void AwaitUnsafeOnCompleted<TAwaiter, TStateMachine>(ref TAwaiter awaiter, ref TStateMachine stateMachine) where TAwaiter: ICriticalNotifyCompletion where TStateMachine: IAsyncStateMachine
{
try
{
// 用於流轉狀態機狀態的 runner
AsyncMethodBuilderCore.MoveNextRunner runnerToInitialize = null;
Action completionAction = this.m_coreState.GetCompletionAction(AsyncCausalityTracer.LoggingOn ? this.Task : null, ref runnerToInitialize);
if (this.m_coreState.m_stateMachine == null)
{
// 此處構建指示異步 Main 方法執行狀態的 Task 對象
Task<TResult> builtTask = this.Task;
this.m_coreState.PostBoxInitialization((TStateMachine) stateMachine, runnerToInitialize, builtTask);
}
awaiter.UnsafeOnCompleted(completionAction);
}
catch (Exception exception)
{
AsyncMethodBuilderCore.ThrowAsync(exception, null);
}
}

咱們一步一步來,先看一下GetCompletionAction的實現:

// 所屬結構體:AsyncMethodBuilderCore

[SecuritySafeCritical]
internal Action GetCompletionAction(Task taskForTracing, ref MoveNextRunner runnerToInitialize)
{
Action defaultContextAction;
MoveNextRunner runner;
Debugger.NotifyOfCrossThreadDependency();
//
ExecutionContext context = ExecutionContext.FastCapture();
if ((context != null) && context.IsPreAllocatedDefault)
{
defaultContextAction = this.m_defaultContextAction;
if (defaultContextAction != null)
{
return defaultContextAction;
}

// 構建 runner
runner = new MoveNextRunner(context, this.m_stateMachine);
// 返回值
defaultContextAction = new Action(runner.Run);
if (taskForTracing != null)
{
this.m_defaultContextAction = defaultContextAction = this.OutputAsyncCausalityEvents(taskForTracing, defaultContextAction);
}
else
{
this.m_defaultContextAction = defaultContextAction;
}
}
else
{
runner = new MoveNextRunner(context, this.m_stateMachine);
defaultContextAction = new Action(runner.Run);
if (taskForTracing != null)
{
defaultContextAction = this.OutputAsyncCausalityEvents(taskForTracing, defaultContextAction);
}
}
if (this.m_stateMachine == null)
{
runnerToInitialize = runner;
}
return defaultContextAction;
}

發現一個熟悉的傢伙——ExecutionContext,它是用來給咱們延續方法(即Console.Write(" World!");)提供運行環境的,注意這裡用的是FastCapture(),該內部方法並未捕獲SynchronizationContext,因為不需要流動它。什麼?你說你不認識它?大眼瞪小眼?那你應該好好看看《理解C#中的ExecutionContext vs SynchronizationContext》了

接著來到new MoveNextRunner(context, this.m_stateMachine),這裡初始化了 runner,我們看看構造函數中做了什麼:

[SecurityCritical]
internal MoveNextRunner(ExecutionContext context, IAsyncStateMachine stateMachine)
{
// 將 ExecutionContext 保存了下來
this.m_context = context;

// 將 stateMachine 保存了下來(不過此時為 null)
this.m_stateMachine = stateMachine;
}

往下來到defaultContextAction = new Action(runner.Run),你可以發現,最終咱們返回的就是這個 defaultContextAction ,所以這個runner.Run至關重要,不過別著急,我們等用到它的時候我們再來看其內部實現。

最後,回到AwaitUnsafeOnCompleted方法,繼續往下走。構建指示異步 Main 方法執行狀態的 Task 對象,設置當前的狀態機後,來到awaiter.UnsafeOnCompleted(completionAction);,要記住,入參 completionAction 就是剛才返回的runner.Run:

// 所屬結構體:TaskAwaiter

[SecurityCritical, __DynamicallyInvokable]
public void UnsafeOnCompleted(Action continuation)
{
OnCompletedInternal(this.m_task, continuation, true, false);
}

[MethodImpl(MethodImplOptions.NoInlining), SecurityCritical]
internal static void OnCompletedInternal(Task task, Action continuation, bool continueOnCapturedContext, bool flowExecutionContext)
{
if (continuation == null)
{
throw new ArgumentNullException("continuation");
}
StackCrawlMark lookForMyCaller = StackCrawlMark.LookForMyCaller;
if (TplEtwProvider.Log.IsEnabled() || Task.s_asyncDebuggingEnabled)
{
continuation = OutputWaitEtwEvents(task, continuation);
}

// 配置延續方法
task.SetContinuationForAwait(continuation, continueOnCapturedContext, flowExecutionContext, ref lookForMyCaller);
}

直接來到代碼最後一行,看到延續方法的配置

// 所屬類:Task

[SecurityCritical]
internal void SetContinuationForAwait(Action continuationAction, bool continueOnCapturedContext, bool flowExecutionContext, ref StackCrawlMark stackMark)
{
TaskContinuation tc = null;
if (continueOnCapturedContext)
{
// 這裡我們用的是不進行流動的 SynchronizationContext
SynchronizationContext currentNoFlow = SynchronizationContext.CurrentNoFlow;
// 像 Winform、WPF 這種框架,實現了自定義的 SynchronizationContext,
// 所以在 Winform、WPF 的 UI線程中進行異步等待時,一般 currentNoFlow 不會為 null
if ((currentNoFlow != null) && (currentNoFlow.GetType() != typeof(SynchronizationContext)))
{
// 如果有 currentNoFlow,那麼我就用它來執行延續方法
tc = new SynchronizationContextAwaitTaskContinuation(currentNoFlow, continuationAction, flowExecutionContext, ref stackMark);
}
else
{
TaskScheduler internalCurrent = TaskScheduler.InternalCurrent;
if ((internalCurrent != null) && (internalCurrent != TaskScheduler.Default))
{
tc = new TaskSchedulerAwaitTaskContinuation(internalCurrent, continuationAction, flowExecutionContext, ref stackMark);
}
}
}
if ((tc == null) & flowExecutionContext)
{
tc = new AwaitTaskContinuation(continuationAction, true, ref stackMark);
}
if (tc != null)
{
if (!this.AddTaskContinuation(tc, false))
{
tc.Run(this, false);
}
}
// 這裡會將 continuationAction 設置為 awaiter 中 task 對象的延續方法,所以當 TestAsync() 完成時,就會執行 runner.Run
else if (!this.AddTaskContinuation(continuationAction, false))
{
AwaitTaskContinuation.UnsafeScheduleAction(continuationAction, this);
}
}

對於我們的示例來說,既沒有自定義 SynchronizationContext,也沒有自定義 TaskScheduler,所以會直接來到最後一個else if (...),重點在於this.AddTaskContinuation(continuationAction, false),這個方法會將我們的延續方法添加到 Task 中,以便於當 TestAsync 方法執行完畢時,執行 runner.Run

2|5runner.Run

好,是時候讓我們看看 runner.Run 的內部實現了:

[SecuritySafeCritical]
internal void Run()
{
if (this.m_context != null)
{
try
{
// 我們並未給 s_invokeMoveNext 賦值,所以 callback == null
ContextCallback callback = s_invokeMoveNext;
if (callback == null)
{
// 將回調設置為下方的 InvokeMoveNext 方法
s_invokeMoveNext = callback = new
ContextCallback(AsyncMethodBuilderCore.MoveNextRunner.InvokeMoveNext);
}
ExecutionContext.Run(this.m_context, callback, this.m_stateMachine, true);
return;
}
finally
{
this.m_context.Dispose();
}
}
this.m_stateMachine.MoveNext();
}

[SecurityCritical]
private static void InvokeMoveNext(object stateMachine)
{
((IAsyncStateMachine) stateMachine).MoveNext();
}

來到ExecutionContext.Run(this.m_context, callback, this.m_stateMachine, true);,這裡的 callback 是InvokeMoveNext方法。所以,當TestAsync執行完畢後,就會執行延續方法 runner.Run,也就會執行stateMachine.MoveNext()促使狀態機繼續進行狀態流轉,這樣邏輯就打通了:

private void MoveNext()
{
// num = 0
int num = this.<>1__state;
try
{
TaskAwaiter awaiter;
if (num != 0)
{
Console.WriteLine("Let's Go!");
awaiter = Program.TestAsync().GetAwaiter();

if (!awaiter.IsCompleted)
{
this.<>1__state = num = 0;
this.<>u__1 = awaiter;
Program.<Main>d__0 stateMachine = this;
this.<>t__builder.AwaitUnsafeOnCompleted<TaskAwaiter, Program.<Main>d__0>(ref awaiter, ref stateMachine);
return;
}
}
else
{
awaiter = this.<>u__1;
this.<>u__1 = new TaskAwaiter();
// 狀態機狀態從 0 流轉到 -1
this.<>1__state = num = -1;
}

// 結束對 TestAsync() 的等待
awaiter.GetResult();
// 執行延續方法
Console.Write(" World!");
}
catch (Exception exception)
{
this.<>1__state = -2;
this.<>t__builder.SetException(exception);
return;
}

// 狀態機狀態從 -1 流轉到 -2
this.<>1__state = -2;
// 設置異步 Main 方法最終返回結果
this.<>t__builder.SetResult();
}

至此,整個異步方法的執行就結束了,通過一張圖總結一下:

最後,我們看一下各個線程的狀態,看看和你的推理是否一致(如果有不清楚的聯繫我,我會通過文字補充):

3|0多個 async await 嵌套

理解了async await的簡單使用,那你可曾想過,如果有多個 async await 嵌套,那會出現什麼情況呢?接下來就改造一下我們的例子,來研究研究:

static Task TestAsync()
{
return Task.Run(async () =>
{
// 增加了這行
await Task.Run(() =>
{
Console.Write("Say: ");
});

Console.Write("Hello");
});
}

反編譯之後的代碼,上面已經講解的我就不再重複貼了,主要看看TestAsync()就行了:

private static Task TestAsync() =>
Task.Run(delegate {
<>c.<<TestAsync>b__1_0>d stateMachine = new <>c.<<TestAsync>b__1_0>d {
<>t__builder = AsyncTaskMethodBuilder.Create(),
<>4__this = this,
<>1__state = -1
};
stateMachine.<>t__builder.Start<<>c.<<TestAsync>b__1_0>d>(ref stateMachine);
return stateMachine.<>t__builder.Task;
});

哦!原來,async await 的嵌套也就是狀態機的嵌套,相信你通過上面的狀態機狀態流轉,也能夠梳理除真正的執行邏輯,那我們就只看一下線程狀態吧:

這也印證了我上面所說的:當子線程完成執行任務時,會被釋放,或回到線程池供其他線程使用。

4|0多個 async await 在同一方法中順序執行

又可曾想過,如果有多個 async await 在同一方法中順序執行,又會是何種景象呢?同樣,先來個例子:

static async Task Main(string[] args)
{
Console.WriteLine("Let's Go!");

await Test1Async();

await Test2Async();

Console.Write(" World!");
}

static Task Test1Async()
{
return Task.Run(() =>
{
Console.Write("Say: ");
});
}

static Task Test2Async()
{
return Task.Run(() =>
{
Console.Write("Hello");
});
}

直接看狀態機:

[CompilerGenerated]
private sealed class <Main>d__0 : IAsyncStateMachine
{
// Fields
public int <>1__state;
public AsyncTaskMethodBuilder <>t__builder;
public string[] args;
private TaskAwaiter <>u__1;

// Methods
private void MoveNext()
{
int num = this.<>1__state;
try
{
TaskAwaiter awaiter;
TaskAwaiter awaiter2;
if (num != 0)
{
if (num == 1)
{
awaiter = this.<>u__1;
this.<>u__1 = default(TaskAwaiter);
this.<>1__state = -1;
goto IL_D8;
}
Console.WriteLine("Let's Go!");
awaiter2 = Program.Test1Async().GetAwaiter();
if (!awaiter2.IsCompleted)
{
this.<>1__state = 0;
this.<>u__1 = awaiter2;
Program.<Main>d__0 <Main>d__ = this;
this.<>t__builder.AwaitUnsafeOnCompleted<TaskAwaiter, Program.<Main>d__0>(ref awaiter2, ref <Main>d__);
return;
}
}
else
{
awaiter2 = this.<>u__1;
this.<>u__1 = default(TaskAwaiter);
this.<>1__state = -1;
}
awaiter2.GetResult();

// 待 Test1Async 完成後,繼續執行 Test2Async
awaiter = Program.Test2Async().GetAwaiter();
if (!awaiter.IsCompleted)
{
this.<>1__state = 1;
this.<>u__1 = awaiter;
Program.<Main>d__0 <Main>d__ = this;
this.<>t__builder.AwaitUnsafeOnCompleted<TaskAwaiter, Program.<Main>d__0>(ref awaiter, ref <Main>d__);
return;
}
IL_D8:
awaiter.GetResult();
Console.Write(" World!");
}
catch (Exception exception)
{
this.<>1__state = -2;
this.<>t__builder.SetException(exception);
return;
}
this.<>1__state = -2;
this.<>t__builder.SetResult();
}

[DebuggerHidden]
private void SetStateMachine(IAsyncStateMachine stateMachine)
{
}
}

可見,就是一個狀態機狀態一直流轉就完事了。我們就看看線程狀態吧:

5|0WPF中使用 async await

上面我們都是通過控制臺舉的例子,這是沒有任何SynchronizationContext的,但是WPF(Winform同理)就不同了,在UI線程中,它擁有屬於自己的DispatcherSynchronizationContext。

private async void Button_Click(object sender, RoutedEventArgs e)
{
// UI 線程會一直保持 Running 狀態,不會導致 UI 假死
Show(Thread.CurrentThread);

await TestAsync();

Show(Thread.CurrentThread);
}

private Task TestAsync()
{
return Task.Run(() =>
{
Show(Thread.CurrentThread);
});
}

private static void Show(Thread thread)
{
MessageBox.Show($"{nameof(thread.ManagedThreadId)}: {thread.ManagedThreadId}" +
$"\n{nameof(thread.ThreadState)}: {thread.ThreadState}");
}

通過使用DispatcherSynchronizationContext執行延續方法,又回到了 UI 線程中

本文作者:xiaoxiaotank
本文連結:https://www.cnblogs.com/xiaoxiaotank/p/14303803.html

版權申明:本文來源於網友收集或網友提供,如果有侵權,請轉告版主或者留言,本公眾號立即刪除。

相關焦點

  • 理解 JavaScript 的 async/await
    我第一次看到這組關鍵字並不是在 JavaScript 語言裡,而是在 c# 5.0 的語法中。C# 的 async/await 需要在 .NET Framework 4.5 以上的版本中使用,因此我還很悲傷了一陣——為了要兼容 XP 系統,我們開發的軟體不能使用高於 4.0 版本的 .NET Framework。我之前在《閒談異步調用「扁平」化》 中就談到了這個問題。
  • C# 中的Async 和 Await 的用法詳解
    在本文中,我們將共同探討並介紹什麼是Async 和 Await,以及如何在C#中使用Async 和 Await。同樣本文的內容也大多是翻譯的,只不過加上了自己的理解進行了相關知識點的補充,如果你認為自己的英文水平還不錯,大可直接跳轉到文章末尾查看原文連結進行閱讀。
  • 理解JavaScript 的 async/await
    1. async 和 await 在幹什麼任意一個名稱都是有意義的,先從字面意思來理解。async 是「異步」的簡寫,而 await 可以認為是 async wait 的簡寫。所以應該很好理解 async 用於申明一個 function 是異步的,而 await 用於等待一個異步方法執行完成。
  • JavaScript中的async/await的用法和理解
    的理解,可能有錯誤之處,也歡迎指出。今天就說一說「JavaScript中的async/await的用法和理解」程式語言中任意一個關鍵字都是有意義的,我們先從字面意思來理解。1.async async 是「異步」的簡寫,帶async關鍵字的函數,是聲明異步函數,返回值是promise對象,如果async關鍵字函數返回的不是promise,會自動用Promise.resolve()包裝。
  • 盤點JavaScript中async/await知識
    一、前言Async/await 是以更舒適的方式使用 promise 的一種特殊語法,同時它也非常易於理解和使用。二、Async function讓以 async 這個關鍵字開始。不能在普通函數中使用 await。
  • 理解C#中的ConfigureAwait
    cnblogs.com/xiaoxiaotank/p/13529413.html前言七年前(原文發布於2019年).NET的程式語言和框架庫添加了async/await語法糖。自那以後,它猶如星火燎原一般,不僅遍及整個.NET生態,還被許許多多的其他語言和框架所借鑑。
  • async/await,了解一下?
    因此,在 ES6 中封裝了 Generator 函數的語法糖 async 函數,但是將其定義在了 es7 中。ES7 定義出的 async 函數,終於讓 JavaScript 對於異步操作有了終極解決方案。 Async 函數是 Generator 函數的語法糖。使用 關鍵字 Async 來表示,在函數內部使用 await 來表示異步。
  • 如何在 JS 循環中正確使用 async 與 await
    async 與 await 的使用方式相對簡單。 當你嘗試在循環中使用await時,事情就會變得複雜一些。在本文中,分享一些在如果循環中使用await值得注意的問題。準備一個例子對於這篇文章,假設你想從水果籃中獲取水果的數量。
  • Swift 的 Async/Await 簡介
    任何潛在的 suspension point 必須發生在異步上下文中(例如,一個 async 函數)。此外,它必須發生在 await 表達式的對象內。給定一個調用,在同步的上下文中,重載解析會優先選擇非 async 函數(因為這種上下文不能包含對 async 函數的調用)。此外,在異步上下文中,重載解析會優先選擇 async 函數(因為這種上下文應該避免從異步模型中跳出而進入阻塞的 API)。當重載解析選擇 async 函數時,該調用仍然需要加上 await。
  • Async/Await有什麼用?
    你可能還注意到了代碼中帶編號的注釋( /*1.*/)本文中的這類注釋可以幫你理解代碼的執行順序。永遠返回 Promise現在我們知道異步函數不是什麼,但是它們是什麼呢?異步函數的第一個超級功能:總是返回一個 promise。
  • [完結篇] - 理解異步之美 --- promise與async await(三)
    因為在用法上promise要比async await難一些,而且promise本身又不是一個語法糖。沒有掌握的時候用起來就會有很多顧慮,async await卻沒有這種顧慮,用法簡單、語義清晰。說到現在很多人應該還沒理解為什麼要說迭代器。Generator函數執行後會返回一個迭代器async函數是Generator的語法糖。這兩個理由是不是一下子就說明寫迭代器是很有用處的。
  • 深入async/await知多少
    在傳統Begin/End的異步模式中所有結果都是由回調函數來處理,當一個邏輯有多層異步代碼時那整個業務邏輯代碼混合著各大和業務無關的回調函數中遊走,這會讓代碼變更難以維護;當有了async/await後這種方式得到了解脫,因為你再不需 在不同Callback函數了維護一簡單的邏輯了;取而代之的就是我們平時普通方法調用一樣,只是在調用間加個await關鍵字。
  • async/await 使用方式
    這時,你可能注意到控制臺中的Promise 有一個resolved,這是async 函數內部的實現原理。如果async 函數中有返回一個值 ,當調用該函數時,內部會調用Promise.solve() 方法把它轉化成一個promise 對象作為返回,但如果timeout 函數內部拋出錯誤呢?
  • 如何正確合理使用 JavaScript async/await
    在本文中,將從不同的角度探討 async/await,並演示如何正確有效地使用這對兄弟。async 作用是什麼從 MDN 可以看出:async 函數返回的是一個 Promise 對象。async 函數調用不會造成阻塞,它內部所有的阻塞都被封裝在一個 Promise 對象中異步執行。async/await 帶給我們的最重要的好處是同步編程風格。讓我們看一個例子:
  • async/await 原理及執行順序分析
    之前寫了篇文章《這一次,徹底理解Promise原理》,剖析了Promise的相關原理。我們都知道,Promise解決了回調地獄的問題,但是如果遇到複雜的業務,代碼裡面會包含大量的 then 函數,使得代碼依然不是太容易閱讀。
  • 壓箱底筆記:Promise和Async/await的理解和使用
    2.1 理解抽象表達:Promise 是JS中進行異步編程的新的解決方案(舊的是誰?async function 用來定義一個返回 AsyncFunction 對象的異步函數。異步函數是指通過事件循環異步執行的函數,它會通過一個隱式的 Promise 返回其結果,。如果你在代碼中使用了異步函數,就會發現它的語法和結構會更像是標準的同步函數。MDN async_functionawait  操作符用於等待一個Promise 對象。
  • JavaScript基礎——深入學習async/await
    本篇文章,小編將和大家一起學習異步編程的未來——async/await,它會打破你對上篇文章Promise的認知,竟然異步代碼還能這麼寫! 但是別太得意,你需要深入理解Promise後,才能更好的的駕馭async/await,因為async/await是基於Promise的,沒有理解Promise,小編強烈建議各位再看看《JavaScript基礎——Promise使用指南》。
  • TOKIO ASYNC&AWAIT 初探
    tokio async&await 初探3.1.1 建立Tcp連接3.1.2 https
  • Python async/await教程
    這個方法在Python3.3中被引入。在Python 3.5中有了更進一步地改進為異步/等待的形式(我們後面會談到)。yield from表達式可以像下面這樣使用:async/await更新的和更清潔的語法是使用async/await關鍵字,async在Python 3.5中引入,用於作為一個協同程序聲明一個函數,就像@asyncio.coroutine裝飾器所做的,通過把它放到函數定義前使它應用於函數:
  • 你必須了解的JavaScript關鍵字async和await
    為了能夠理解本文的內容,您需要對promises和generators有一個紮實的理解。這些資源應該能幫助你。Promise 假設我們有如下代碼。在這裡,我用一個Promise包裝一個HTTP請求。注意,await只能在用async關鍵字標記的函數中使用。它的工作方式與Generator類似,在Promise完成之前暫停上下文中的執行。如果等待的表達不是Promise,那麼它就變成了Promise。