Home > Back-end >  Unity Coroutine is not starting when called from an event handler
Unity Coroutine is not starting when called from an event handler

Time:03-30

I am having a problem while trying to call StartCoroutine from the body of an event handler. I am expecting to print the following on the console, instead it only prints "before" and it does not start the coroutine:

  1. before

  2. inside

  3. working

  4. after

    private void Awake()
    {
        socket.OnMessage  = (sender,e) => {
            Debug.Log("before");
            StartCoroutine(handleIncommingMessages(sender,e));
            Debug.Log("after");
        };
    }
    
    public IEnumerator handleIncommingMessages(object sender, MessageEventArgs e) {
    
        Debug.Log("inside");
        yield return new WaitForSeconds(3f);
        Debug.Log("working");
    
    }
    

CodePudding user response:

There might be issues with the Coroutine being discarded once the event call finishes. I do not know how to fix it - but if you are using a newer version of Unity you can use tasks instead:

private void Awake()
{
    // We add async so that we can use the `await` call.
    socket.OnMessage  = async (sender,e) => { 
        Debug.Log("before");
        
        // Force it to await when the incoming message as not to close the thread
        await handleIncommingMessages(sender, e);
        
        Debug.Log("after");
    };
}

public async Task handleIncommingMessages(object sender, MessageEventArgs e) {

    Debug.Log("inside");
    await Task.Delay(1000);
    Debug.Log("working");

}

If that doesn't work try: public async void handleIncommingMessages instead of Task

CodePudding user response:

This coroutine only works in main thread. You have to inject the method of the coroutine you want to execute to the main thread.

Usually when you try to run it in another thread you get this error: UnityException: IsObjectMonoBehaviour can only be called from the main thread.

I solved this by defining a public List and using Update method.

List<Action> threadPumpList = new List<Action>();

private void Update()
{
    while (threadPumpList.Count > 0)
    {
        threadPumpList[0].Invoke();
        threadPumpList.RemoveAt(0);
    }
}

public void WriteTestMessage()
{
    Debug.Log("Hello World.")
}

public void Test()
{
    threadPumpList.Add(() => WriteTestMessage());
}

You can use this like this.

List<IEnumerator> threadPumpList = new List<IEnumerator>();

private void Update()
{
    while (threadPumpList.Count > 0)
    {
        StartCoroutine(threadPumpList[0]);
        threadPumpList.RemoveAt(0);
    }
}

private void Awake()
{
    //socket.OnMessage  = (sender,e) =>
    new Thread(() => //You should use socket right here.
      {
        Debug.Log("before");
        threadPumpList.Add(handleIncommingMessages(null, ""));
        Debug.Log("after");
    }).Start();
}


public IEnumerator handleIncommingMessages(object sender, string e) //I changed parameters for testing.
{

    Debug.Log("inside");
    yield return new WaitForSeconds(3f);
    Debug.Log("working");

}

When I test it the output is like this:

-before -after -inside (3 seconds wait) -working

  • Related