Using `async` functions from synchronous functions (and breaking all the rules)

You can use a class to box the returned value. Something like this works (with all the safety caveats mentioned):

fileprivate class Box<ResultType> {
	var result: Result<ResultType, Error>? = nil
}

/// Unsafely awaits an async function from a synchronous context.
@available(*, deprecated, message: "Migrate to structured concurrency")
func _unsafeWait<ResultType>(_ f: @escaping () async throws -> ResultType) throws -> ResultType {
	let box = Box<ResultType>()
	let sema = DispatchSemaphore(value: 0)
	Task {
		do {
			let val = try await f()
			box.result = .success(val)
		} catch {
			box.result = .failure(error)
		}
		sema.signal()
	}
	sema.wait()
	return try box.result!.get()
}

(You can also make a non-throwing version if needed. I don't think there's a way to make rethrows work in this way).

Then can call:

// in a sync context
let data = try _unsafeWait {
	let (data, _) = try await URLSession.shared.data(from: url)
	return data
}
4 Likes