2017-08-04 86 views
1

認爲我必須從服務器獲取登錄信息,然後登錄,那麼以後登錄下一步做什麼。目標是將所有這些封裝成調用者可以等待的東西。任務在火力地堡團結鏈接,如何避免代碼金字塔?

但是,每個Firebase C#API調用都已返回單獨的任務(已經啓動)。我的方法是考慮如何將這些單獨的任務鏈接在一起。

我想製作一個新Task表示這一點,因爲它似乎也非常適合聚集所有呼叫者趕上例外。

我們有

Task<DataSnapshot> getKeyTask = Database.Child("keyLocation").GetValueAsync();

GetValueAsync據我瞭解已經紡出任務。我們可以觀察返回的任務,或者更好地使用延續來完成任務。

Task loginTask = Auth.SignInWithEmailAndPasswordAsync(username, password);

Auth.SignInWithEmailAndPasswordAsync也轉動了任務本身。我也可以使用continuation來等待並開始其他事情。

登錄後我想用另一個數據庫調用來獲取用戶數據。

所以

public Task Login() 
{ 
    Task<DataSnapshot> getKeyTask = Database.Child("keyLocation").GetValueAsync(); 

    Task loginAfterGetKeyTask = getKeyTask.ContinueWith(gkt => 
    { 
     Task loginTask = Auth.SignInWithEmailAndPasswordAsync(gkt.Result.___.username, gkt.Result.____.password); 
     loginTask.ContinueWith(lt => { ....the next task.... }); 
    }); 

    return loginAfterGetKeyTask; 
} 

有2個問題,我發現:

  1. 隨着它去上的代碼被越來越金字塔更深。有什麼辦法可以讓代碼看起來更順序嗎?
  2. Login()函數我想返回一個Task,以便調用者可以等待整個登錄過程完成然後繼續等等。但是,當前返回的loginAfterGetKey任務完成,即使裏面還有更多事情要做。我可以繼續在外面做.ContinueWith鏈接,如果不是有被執行的一個更火力地堡的任務。理想情況下,我想返回代表整個事情Task但他們嵌套。火力地堡的方法來啓動它自己的任務,所以我想我只能看不能捆綁我自己所有的任務。

我試圖封裝所有的任務作爲一個新的任務從Task.Factory.StartNew開始但是「GetComponent只能從主線程調用」是一個問題。 (該Auth和其他火力地堡服務將需要GetComponent

我嘗試使用IEnumeratorLogin()返回,並計劃把yield這個功能,但是,「產量語句不能匿名方法或lambda表達式中使用」。

我試圖以使loginAfterGetKeyTask,我從這個函數返回使用loginTask.Wait()第一ContinueWith內不IsCompleted狀態到達,直到它reacehs結束(這將必須等待裏面的任務),但因爲在這種拉姆達ContinueWith在主線程中,它會導致死鎖。

+0

那麼爲什麼不使用'async/await'? – Nkosi

+0

我已閱讀過關於它們的信息,但不幸的是,Unity的C#版本無法使用這些關鍵字。 – 5argon

+0

噢好吧我不知道。 – Nkosi

回答

1

最後我用IEnumerator循環等待作爲一種破解等待Task完成。它將多線程系統「線性化」爲Unity似乎更喜歡的單線程協同系統。每個yield return null導致在下一幀中恢復以再次檢查Task的狀態。

不是很優雅,但我找不到async/await以外的任何更好的方式與Task配對,但Unity的C#版本尚不支持它。

public void Caller() 
{ 
    yield return StartCoroutine(Login()); 
    // Login finished, check the login credentials 
} 

private IEnumerator WaitTask(Task task) 
{ 
    while (task.IsCompleted == false) 
    { 
     yield return null; 
    } 
    if(task.IsFaulted) 
    { 
     throw task.Exception; 
    } 
} 

public IEnumerator Login() 
{ 
    Task<DataSnapshot> getKeyTask = Database.Child("keyLocation").GetValueAsync(); 

    yield return WaitTask(getKeyTask); 

    Task loginTask = Auth.SignInWithEmailAndPasswordAsync(getKeyTask.Result.___.username, getKeyTask.Result.____.password); 

    yield return WaitTask(loginTask); 

    //... the next task can use loginTask.Result etc. 

} 

,因爲編譯器不允許try catch包裹yield這將是棘手的還是捕捉異常。我可以使用回調模式將異常發送給調用者。

這裏是一個擴展版本,以防你想像yield return task.YieldWait();那樣做。

public static class TaskExtension 
{ 
    /// <summary> 
    /// Firebase Task might not play well with Unity's Coroutine workflow. You can now yield on the task with this. 
    /// </summary> 
    public static IEnumerator YieldWait(this Task task) 
    { 
     while (task.IsCompleted == false) 
     { 
      yield return null; 
     } 
     if(task.IsFaulted) 
     { 
      throw task.Exception; 
     } 
    } 
} 
+1

您現在正在檢查每個任務的狀態與原始代碼不同,它在異步操作正在進行時根本不會執行任何操作。 – Servy