 /// Promises start in a *pending* state and either get *fulfilled* with a
 /// value or get *rejected* with an error.
 public final class Promise<T> {
-    private var state: State<T> = .pending(Handlers())
-    private let lock = NSLock()
+    // previous `State` based approach was easier to reason about but was
+    // harder to implement in a performant way.
+    private var handlers: Handlers<T>? // nil when finished
+    private var result: Result<T>? // nil when pending
 
     // MARK: Creation
 

        // `fulfill` and `reject` are called asynchronously on the `queue`
        let _fulfill: (T) -> Void = { value in queue.async { fulfill(value) } }
        let _reject: (Error) -> Void = { error in queue.async { reject(error) } }
        
        lock.lock(); defer { lock.unlock() }
        switch state {
        case let .pending(handlers):
            handlers.fulfill.append(_fulfill)
            handlers.reject.append(_reject)
        case let .fulfilled(value): _fulfill(value)
        case let .rejected(error): _reject(error)
        }
    }
    
    // MARK: Then
    
    /// The given closure executes asynchronously when the promise is fulfilled.

    var reject = [(Error) -> Void]()
}

private enum Result<T> {
    case fulfilled(T), rejected(Error)
    /// Returns a `value` if the result is success.

    var value: T? {
        if case let .fulfilled(val) = self { return val } else { return nil }
    }

    /// Returns an `error` if the result is failure.
    var error: Error? {
        if case let .rejected(err) = self { return err } else { return nil }
    }
}

// We use the same lock across different tokens because the design of Promise
// prevents potential issues. For example, closures registered with a Promise
// are never executed inside a lock.
private let _lock = NSLock()
