Iterators (functions that yield) are great for representing asynchronous processes such as web calls, updating state over time, and multi-step operations. One unfortunate downside is that it’s tough to return a result from them. Today’s article explores several workarounds to this problem before ultimately arriving at a clean, easy-to-use solution to the problem. Read on to learn how to make iterators more useful!

Here’s an example iterator for retrieving a text document from the web:

IEnumerable RetrieveWebTextDocument(string url)
{
	var www = new WWW(url);
	yield return www;
	var text = www.text;
	// PROBLEM- how to get 'text' to the caller
}

This is a really common problem faced by Unity programmers. The two most obvious “solutions” don’t even compile:

// Attempt #1: return the result
IEnumerable RetrieveWebTextDocument(string url)
{
	var www = new WWW(url);
	yield return www;
	var text = www.text;
	return text;
}
 
// Attempt #2: use 'out' or 'ref' parameters
IEnumerable RetrieveWebTextDocument(string url, out string text)
{
	var www = new WWW(url);
	yield return www;
	text = www.text;
}

Attempt #1 doesn’t work because the return type isn’t string. That’s because the return type has to be IEnumerable, IEnumerable<T>, IEnumerator, or IEnumerator<T>. So the ultimate normal “solution” simply doesn’t work.

Attempt #2 doesn’t work because C# forbids using out or ref parameters in an iterator.

At this point most Unity programmers that I’ve seen faced with this problem work around it in one of two ways. They either replace the “PROBLEM” line with the actual code that uses the text document or a call to a function that uses it:

// Option #1: directly use the result
IEnumerable RetrieveWebTextDocument(string url)
{
	var www = new WWW(url);
	yield return www;
	var text = www.text;
	Debug.Log("Text document: " + text);
}
 
// Option #2: call the result-handling function
IEnumerable RetrieveWebTextDocument(string url)
{
	var www = new WWW(url);
	yield return www;
	var text = www.text;
	HandleWebTextDocumentRetrieved(text);
}
 
void HandleWebTextDocumentRetrieved(string text)
{
	Debug.Log("Text document: " + text);
}

Both of these have the same problem. In both cases the iterator has direct knowledge of the code that handles the result. That’s fine if you’re only intending for the function to be used one time, but what if you wanted to use it 10 times and handle the result differently each time? After all, RetrieveWebTextDocument is a generalized function that has nothing to do with the code that handles its result. By tying the iterator’s code and the handler’s code together we’ve made reuse of the iterator much harder.

One way to make the iterator reusable is to use a handler function like in option #2 but take it as an parameter to the function rather than hard-coding it. Here’s how that would look:

// Option #3: call the result-handling function as a parameter
IEnumerable RetrieveWebTextDocument(string url, Action<string> resultHandler)
{
	var www = new WWW(url);
	yield return www;
	var text = www.text;
	resultHandler(text);
}
 
void HandleWebTextDocumentRetrieved(string text)
{
	Debug.Log("Text document: " + text);
}

The two parts—iterator and result handler—are now separate and the iterator is now reusable for an arbitrary number of handler functions. This is a very good solution that is straightforward to implement and may well be enough for most Unity programmers. However, there is an alternate approach that some may find even more appealing.

What if the iterator could simply yield return its result? That would free the iterator from needing to take a result handler parameter, cleaning up the API by removing an input to the function that is really an output. By doing this, it allows us to rewrite RetrieveWebTextDocument to just yield return the result:

// Option #4: yield return the result
IEnumerable RetrieveWebTextDocument(string url)
{
	var www = new WWW(url);
	yield return www;
	var text = www.text;
	yield return text;
}

With option #4 we’ve almost got back to the simplest possible version (attempt #1), except that this actually compiles! The only difference is that we need to yield return the result rather than just using return. Unfortunately, there’s one big problem with this: using it is awkward and verbose.

IEnumerable PrintGoogleHomepage()
{
	object yieldedVal = null;
	foreach (var cur in RetrieveWebTextDocument("http://google.com"))
	{
		yieldedVal = cur;
		yield return cur;
	}
	var text = (string)yieldedVal;
	Debug.Log("Text document: " + text);
}

PrintGoogleHomepage is an iterator, too. It uses the actual iterator (RetrieveWebTextDocument) and iterates over it with foreach, yielding each step of the way. It keeps track of the last value yielded by the iterator it’s iterating over. In the end, it directly uses the result.

This is essentially just like option #1 of RetrieveWebTextDocument except that its asynchronous work is to iterate over an IEnumerable rather than use a WWW. So what if we passed in the result handler in the style of option #3?

IEnumerable GetGoogleHomepage(Action<string> resultHandler)
{
	object yieldedVal = null;
	foreach (var cur in RetrieveWebTextDocument("http://google.com"))
	{
		yieldedVal = cur;
		yield return cur;
	}
	var text = (string)yieldedVal;
	resultHandler(text);
}

You could now use it like so:

StartCoroutine(GetGoogleHomepage(text => Debug.Log("Google homepage: " + text)));

GetGoogleHomepage is now much more reusable, but still tied to a particular iterator function (RetrieveWebTextDocument) and it only handles string results. So let’s evolve it to take the iterator function as a parameter. It’s now generic enough that we can call it GetResult.

IEnumerable GetResult(IEnumerable iterator, Action<object> resultHandler)
{
	object last = null;
	foreach (var cur in iterator)
	{
		last = cur;
		yield return cur;
	}
	resultHandler(last);
}

Usage now looks like this:

StartCoroutine(
	GetResult(RetrieveWebTextDocument("http://google.com")).GetEnumerator(),
	result => Debug.Log("Google homepage: " + ((string)result))
);

GetResult is a lot better than its predecessor, but still has one problem: it can only output untyped object results. This invariably leads to casting the result to the appropriate type and all the problems that brings along with it. Fortunately, we can easily work around this by utilizing the fact that iterators can return IEnumerable<T> to specify what type they will be yielding. So let’s tweak RetrieveWebTextDocument once more to yield string results:

// Option #5: yield return the result as a string
IEnumerable<string> RetrieveWebTextDocument(string url)
{
	var www = new WWW(url);
	yield return www;
	var text = www.text;
	yield return text;
}

Of course we’ll also have to upgrade GetResult to call result handlers that take a matching type:

IEnumerable<T> GetResult<T>(IEnumerable<T> iterator, Action<T> resultHandler)
{
	T last = default(T);
	foreach (var cur in iterator)
	{
		last = cur;
		yield return cur;
	}
	resultHandler(last);
}

The end result is that we can eliminate the cast in the code that uses this:

StartCoroutine(
	GetResult(RetrieveWebTextDocument("http://google.com")).GetEnumerator(),
	text => Debug.Log("Google homepage: " + text)
);

Since it’s really common to use coroutines to run iterator functions, it’d be nice to have a helper function to clean up the usage code even more by eliminating the need to call GetEnumerator and StartCoroutine. Here’s one such helper function:

IEnumerable<T> GetResult<T>(
	MonoBehaviour monoBehaviour,
	IEnumerable<T> iterator,
	Action<T> resultHandler
)
{
	var enumerator = GetResult(iterator, resultHandler).GetEnumerator();
	monoBehaviour.StartCoroutine(enumerator);
}

The code using this now looks like…

GetResult(
	this,
	RetrieveWebTextDocument("http://google.com"),
	text => Debug.Log("Google homepage: " + text)
);

With this step the question is now where to put the GetResult functions. C# extension methods seem like a natural fit for these sort of utility functions, so let’s make a static class for those. Here’s the final result for this article:

using System;
using System.Collections;
using System.Collections.Generic;
 
using UnityEngine;
 
// Extension methods to get the result of an iterator. A result
// is the last value that it yielded.
// by Jackson Dunstan, http://JacksonDunstan.com/articles/3099
public static class IteratorResultExtensions
{
	// Iterate then call an Action with the result
	public static IEnumerable<T> GetResult<T>(
		this IEnumerable<T> enumerable,
		Action<T> callback
	)
	{
		T last = default(T);
		foreach (var cur in enumerable)
		{
			last = cur;
			yield return cur;
		}
		callback(last);
	}
 
	// Start a coroutine to iterate then an Action with the result
	public static void GetResult<T>(
		this IEnumerable<T> enumerable,
		MonoBehaviour monoBehaviour,
		Action<T> callback
	)
	{
		var enumerator = enumerable.GetResult(callback).GetEnumerator();
		monoBehaviour.StartCoroutine(enumerator);
	}
}

Here’s how you use it:

RetrieveWebTextDocument("http://google.com").GetResult(
	this,
	text => Debug.Log("Google homepage: " + text)
);

That wraps up today’s article exploring various ways to get a result out of an iterator. I think the end result makes it really easy and clean, which should bolster the number of cases where I want to use iterators to represent asynchronous processes. But what about you? Do you have a particular technique you use to perform asynchronous tasks? If you use iterators, do you use them like in this article? Share your strategies in the comments!