samanthaj
samanthaj

Reputation: 661

Swift equivalent of JavaScript/C# generator functions/yield

In JavaScript I can easily make a complex function give up execution in the middle and continue it later.

Example:

function* nested() {
  for (let i = 0; i < 5; ++i) {
    for (let j = 0; j < 5; ++j) {
      for (let k = 0; k < 5; ++k) {
        yield `${i},${j},${k}`;
      }
    }
  }
}

const n = nested();
function runOneStepEachFrame() {
  const {done, value} = n.next();
  if (done) {
    console.log('done');
  } else {
    console.log(value);
    requestAnimationFrame(runOneStepEachFrame);
  } 
}
runOneStepEachFrame();

C#

using System;
using System.Collections.Generic;
                    
public class Program
{
    public static IEnumerable<string> Nested()
    {
      for (int i = 0; i < 5; ++i) {
        for (int j = 0; j < 5; ++j) {
          for (int k = 0; k < 5; ++k) {
            yield return String.Format("{0},{1},{2}", i, j, k);
          }
        }
      }
    }

    
    public static void Main()
    {
       foreach(string s in Nested())
       {
         Console.WriteLine(s);
       }
    }
}

Does Swift have a similar functionality? Of course I can refactor to keep my own state but my actual function is a fairly complex calculation and I'd like to "yield" in the middle of it (and not yield in the sense of suspending a thread, but in the sense of a generator as above)

Upvotes: 1

Views: 392

Answers (1)

Sweeper
Sweeper

Reputation: 273223

AsyncStream is very similar, in terms of syntax, to what you are looking for, which I believe is coroutines for sequences. Note that one big difference between AsyncStream and iterator methods in C# (I don't know about JavaScript) is that AsyncStream is asynchronous. One could say that it is more similar to IAsyncEnumerable<T> :)

For example, the C# code can be rewritten like this:

let sequence = AsyncStream { continuation in
    for i in 0..<5 {
        for j in 0..<5 {
            for k in 0..<5 {
                continuation.yield("\(i) \(j) \(k)")
            }
        }
    }
    continuation.finish()
}
Task {
    for await elem in sequence {
        print(elem)
    }
}

Here is a discussion on forums.swift.org about synchronous coroutines, and to quote one of the comments there:

Synchronous coroutines are, I believe, being planned for in a "whenever we can find the time for it" sort of way. If I remember correctly, that's why _read & _modify use yield to give up their storage. Sadly, I don't think there are any immediate plans to add them in.

Upvotes: 3

Related Questions