一文带你使用 Swift 实现 Promise

开发 前端
我最近在找如何使用 Swift 实现 Promise 的资料,因为没找到好的文章,所以我想自己写一篇。通过本文,我们将实现自己的 Promise 类型,以便明了其背后的逻辑。

[[421175]]

前言

我最近在找如何使用 Swift 实现 Promise 的资料,因为没找到好的文章,所以我想自己写一篇。通过本文,我们将实现自己的 Promise 类型,以便明了其背后的逻辑。

要注意这个实现完全不适合生产环境。例如,我们的 Promise 没有提供任何错误机制,也没有覆盖线程相关的场景。我会在文章的后面提供一些有用的资源以及完整实现的链接,以飨愿深入挖掘的读者。

注:为了让本教程更有趣一点,我选择使用 TDD 来进行介绍。我们会先写测试,然后确保它们一个个通过。

第一个测试

先写第一个测试:

test(named: "0. Executor function is called immediately") { assert, done in 
    var string: String = "" 
    _ = Promise { string = "foo" } 
    assert(string == "foo"
    done() 

  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.

通过此测试,我们想实现:传递一个函数给 Promise 的初始化函数,并立即调用此函数。

注:我们没有使用任何测试框架,仅仅使用一个自定义的test方法,它在 Playground 中模拟断言(gist[1])。

当我们运行 Playground,编译器会报错:

error: Promise.playground:41:9: error: use of unresolved identifier 'Promise' 
    _ = Promise { string = "foo" } 
        ^~~~~~~ 
  • 1.
  • 2.
  • 3.

合理,我们需要定义 Promise 类。

class Promise { 
 

  • 1.
  • 2.
  • 3.

再运行,错误变为:

error: Promise.playground:44:17: error: argument passed to call that takes no arguments 
    _ = Promise { string = "foo" } 
                ^~~~~~~~~~~~~~~~~~ 
  • 1.
  • 2.
  • 3.

我们必须定义一个初始化函数,它接受一个闭包作为参数。而且这个闭包应该被立即调用。

class Promise { 
 
    init(executor: () -> Void) { 
        executor() 
    } 

  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.

由此,我们通过第一个测试。目前我们还没有写出什么值得夸耀的东西,但耐心一点,我们的实现将在下一节继续增长。

• Test 0. Executor function is called immediately passed  
  • 1.

我们先将此测试注释掉,因为将来的 Promise 实现会变得有些不同。

最低限度

第二个测试如下:

test(named: "1.1 Resolution handler is called when promise is resolved sync") { assert, done in 
    let string: String = "foo" 
    let promise = Promise<String> { resolve in 
        resolve(string) 
    } 
    promise.then { (value: String) in 
        assert(string == value) 
        done() 
    } 

  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.

这个测试挺简单,但我们添加了一些新内容到 Promise 类。我们创建的这个 promise 有一个 resolution handler(即闭包的resolve参数),之后立即调用它(传递一个 value)。然后,我们使用 promise 的 then 方法来访问 value 并用断言确保其值。

在开始实现之前,我们需要引入另外一个不太一样的测试。

test(named: "1.2 Resolution handler is called when promise is resolved async") { assert, done in 
    let string: String = "foo" 
    let promise = Promise<String> { resolve in 
        after(0.1) { 
            resolve(string) 
        } 
    } 
    promise.then { (value: String) in 
        assert(string == value) 
        done() 
    } 

  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.

不同于测试 1.1,这里的resove方法被延迟调用。这意味着,在then里,value 不会立马可用(因为 0.1 秒的延迟,调用then时,resolve还未被调用)。

我们开始理解这里的“问题”。我们必须处理异步。

我们的 promise 是一个状态机。当它被创建时,promise 处于pending状态。一旦resolve方法被调用(与一个 value),我们的 promise 将转到resolved状态,并存储这个 value。

then方法可在任意时刻被调用,而不管 promise 的内部状态(即不管 promise 是否已有一个 value)。当这个 promise 处于pending状态时,我们调用then,value 将不可用,因此,我们需要存储此回调。之后一旦 promise 变成resolved,我们就能使用 resolved value 来触发同样的回调。

现在我们对要实现的东西有了更好的理解,那就先以修复编译器的报错开始。

error: Promise.playground:54:19: error: cannot specialize non-generic type 'Promise' 
    let promise = Promise<String> { resolve in 
                  ^      ~~~~~~~~ 
  • 1.
  • 2.
  • 3.

我们必须给Promise类型添加泛型。诚然,一个 promise 是这样的东西:它关联着一个预定义的类型,并能在被解决时,将一个此类型的 value 保留住。

class Promise<Value> { 
 
    init(executor: () -> Void) { 
        executor() 
    } 

  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.

现在错误为:

error: Promise.playground:54:37: error: contextual closure type '() -> Void' expects 0 arguments, but 1 was used in closure body 
    let promise = Promise<String> { resolve in 
                                    ^ 
  • 1.
  • 2.
  • 3.

我们必须提供一个resolve函数传递给初始化函数(即 executor)。

class Promise<Value> { 
 
    init(executor: (_ resolve: (Value) -> Void) -> Void) { 
        executor() 
    } 

  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.

注意这个 resolve 参数是一个函数,它消耗一个 value:(Value) -> Void。一旦 value 被确定,这个函数将被外部世界调用。

编译器依然不开心,因为我们需要提供一个resolve函数给executor。让我们创建一个private的吧。

class Promise<Value> { 
 
    init(executor: (_ resolve: @escaping (Value) -> Void) -> Void) { 
        executor(resolve) 
    } 
 
    private func resolve(_ value: Value) -> Void { 
        // To implement 
        // This will be called by the outside world when a value is determined 
    } 

  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.

我们将在稍后实现resolve,当所有错误都被解决时。

下一个错误很简单,方法then还未定义。

error: Promise.playground:61:5: error: value of type 'Promise<String>' has no member 'then' 
    promise.then { (value: String) in 
    ^~~~~~~ ~~~~ 
  • 1.
  • 2.
  • 3.

让我们修复之。

class Promise<Value> { 
 
    init(executor: (_ resolve: @escaping (Value) -> Void) -> Void) { 
        executor(resolve) 
    } 
 
    func then(onResolved: @escaping (Value) -> Void) { 
        // To implement 
    } 
 
    private func resolve(_ value: Value) -> Void { 
        // To implement 
    } 

  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.

现在编译器开心了,让我们回到开始的地方。

我们之前说过一个Promise就是一个状态机,它有一个pending状态和一个resolved状态。我们可以使用 enum 来定义它们。

enum State<T> { 
    case pending 
    case resolved(T) 

  • 1.
  • 2.
  • 3.
  • 4.

Swift 的美妙让我们可以直接存储 promise 的 value 在 enum 中。

现在我们需要在Promise的实现中定义一个状态,其默认值为.pending。我们还需要一个私有函数,它能在当前还处于.pending状态时更新状态。

class Promise<Value> { 
 
    enum State<T> { 
        case pending 
        case resolved(T) 
    } 
 
    private var state: State<Value> = .pending 
 
    init(executor: (_ resolve: @escaping (Value) -> Void) -> Void) { 
        executor(resolve) 
    } 
 
    func then(onResolved: @escaping (Value) -> Void) { 
        // To implement 
    } 
 
    private func resolve(_ value: Value) -> Void { 
        // To implement 
    } 
 
    private func updateState(to newState: State<Value>) { 
        guard case .pending = state else { return } 
        state = newState 
    } 

  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.
  • 20.
  • 21.
  • 22.
  • 23.
  • 24.
  • 25.
  • 26.

注意updateState(to:)函数先检查了当前处于.pending状态。如果 promise 已经处于.resolved状态,那它就不能再变成其他状态了。

现在是时候在必要时更新 promise 的状态,即,当resolve函数被外部世界传递 value 调用时。

private func resolve(_ value: Value) -> Void { 
    updateState(to: .resolved(value)) 

  • 1.
  • 2.
  • 3.

快好了,只缺少 then 方法还未实现。我们说过必须存储回调,并在 promise 被解决时调用回调。这就来实现之。

class Promise<Value> { 
 
    enum State<T> { 
        case pending 
        case resolved(T) 
    } 
 
    private var state: State<Value> = .pending 
    // we store the callback as an instance variable 
    private var callback: ((Value) -> Void)? 
 
    init(executor: (_ resolve: @escaping (Value) -> Void) -> Void) { 
        executor(resolve) 
    } 
 
    func then(onResolved: @escaping (Value) -> Void) { 
        // store the callback in all cases 
        callback = onResolved 
        // and trigger it if needed 
        triggerCallbackIfResolved() 
    } 
 
    private func resolve(_ value: Value) -> Void { 
        updateState(to: .resolved(value)) 
    } 
 
    private func updateState(to newState: State<Value>) { 
        guard case .pending = state else { return } 
        state = newState 
        triggerCallbackIfResolved() 
    } 
 
    private func triggerCallbackIfResolved() { 
        // the callback can be triggered only if we have a value, 
        // meaning the promise is resolved 
        guard case let .resolved(value) = state else { return } 
        callback?(value) 
        callback = nil 
    } 

  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.
  • 20.
  • 21.
  • 22.
  • 23.
  • 24.
  • 25.
  • 26.
  • 27.
  • 28.
  • 29.
  • 30.
  • 31.
  • 32.
  • 33.
  • 34.
  • 35.
  • 36.
  • 37.
  • 38.
  • 39.
  • 40.

我们定义了一个实例变量callback,以在 promise 处于.pending状态时保留回调。同时我们创建一个方法triggerCallbackIfResolved,它先检查状态是否为.resolved,然后传递拆包的 value 给回调。这个方法在两个地方被调用。一个是then方法中,如果 promise 已经在调用then时被解决。另一个在updateState方法中,因为那是 promise 更新其内部状态从.pending到.resolved的地方。

有了这些修改,我们的测试就成功通过了。

• Test 1.1 Resolution handler is called when promise is resolved sync passed (1 assertions) 
• Test 1.2 Resolution handler is called when promise is resolved async passed (1 assertions) 
  • 1.
  • 2.

我们走对了路,但我们还需要做出一点改变,以得到一个真正的Promise实现。先来看看测试。

test(named: "2.1 Promise supports many resolution handlers sync") { assert, done in 
    let string: String = "foo" 
    let promise = Promise<String> { resolve in 
        resolve(string) 
    } 
    promise.then { value in 
        assert(string == value) 
    } 
    promise.then { value in 
        assert(string == value) 
        done() 
    } 

  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
test(named: "2.2 Promise supports many resolution handlers async") { assert, done in 
    let string: String = "foo" 
    let promise = Promise<String> { resolve in 
        after(0.1) { 
            resolve(string) 
        } 
    } 
    promise.then { value in 
        assert(string == value) 
    } 
    promise.then { value in 
        assert(string == value) 
        done() 
    } 

  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.

这回我们对每个 promise 都调用了两次then。

先看看测试输出。

• Test 2.1 Promise supports many resolution handlers sync passed (2 assertions) 
• Test 2.2 Promise supports many resolution handlers async passed (1 assertions) 
  • 1.
  • 2.

虽然测试通过了,但你可能也注意问题。测试 2.2 只有一个断言,但应该是两个。

如果我们思考一下,这其实符合逻辑。诚然,在异步的测试 2.2 中,当第一个then被调用时,promise 还处于.pending状态。如我们之前所见,我们存储了第一次then的回调。但当我们第二次调用then时,promise 还是没有被解决,依然处于.pending状态,于是,我们将回调擦除换成了新的。只有第二个回调会在将来被执行,第一个被忘记了。这使得测试虽然通过,但只有一个断言而不是两个。

解决办法也很简单,就是存储一个回调的数组,并在promise被解决时触发它们。

让我们更新一下。

class Promise<Value> { 
 
    enum State<T> { 
        case pending 
        case resolved(T) 
    } 
 
    private var state: State<Value> = .pending 
    // We now store an array instead of a single function 
    private var callbacks: [(Value) -> Void] = [] 
 
    init(executor: (_ resolve: @escaping (Value) -> Void) -> Void) { 
        executor(resolve) 
    } 
 
    func then(onResolved: @escaping (Value) -> Void) { 
        callbacks.append(onResolved) 
        triggerCallbacksIfResolved() 
    } 
 
    private func resolve(_ value: Value) -> Void { 
        updateState(to: .resolved(value)) 
    } 
 
    private func updateState(to newState: State<Value>) { 
        guard case .pending = state else { return } 
        state = newState 
        triggerCallbacksIfResolved() 
    } 
 
    private func triggerCallbacksIfResolved() { 
        guard case let .resolved(value) = state else { return } 
        // We trigger all the callbacks 
        callbacks.forEach { callback in callback(value) } 
        callbacks.removeAll() 
    } 

  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.
  • 20.
  • 21.
  • 22.
  • 23.
  • 24.
  • 25.
  • 26.
  • 27.
  • 28.
  • 29.
  • 30.
  • 31.
  • 32.
  • 33.
  • 34.
  • 35.
  • 36.
  • 37.

测试通过,而且都有两个断言。

• Test 2.1 Promise supports many resolution handlers sync passed (2 assertions) 
• Test 2.2 Promise supports many resolution handlers async passed (2 assertions) 
  • 1.
  • 2.

恭喜!我们已经创建了自己的Promise类。你已经可以使用它来抽象异步逻辑,但它还有限制。

注:如果从全局来看,我们知道then可以被重命名为observe。它的目的是消费 promise 被解决后的 value,但它不返回什么。这意味着我们暂时没法串联多个 promise。

串联多个 Promise

如果我们不能串联多个 promise,那我们的Promise实现就不算完整。

先来看看测试,它将帮助我们实现这个特性。

test(named: "3. Resolution handlers can be chained") { assert, done in 
    let string: String = "foo" 
    let promise = Promise<String> { resolve in 
        after(0.1) { 
            resolve(string) 
        } 
    } 
    promise 
        .then { value in 
            return Promise<String> { resolve in 
                after(0.1) { 
                    resolve(value + value) 
                } 
            } 
        } 
        .then { value in // the "observe" previously defined 
            assert(string + string == value) 
            done() 
        } 

  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.
  • 20.

如测试所见,第一个then创建了一个有新 value 的新Promise并返回了它。第二个then(我们前一节定义的,被称为observe)被串联在后面,它访问新的 value(其将是"foofoo")。

我们很快在终端里看到错误。

error: Promise.playground:143:10: error: value of tuple type '()' has no member 'then' 
        .then { value in 
         ^ 
  • 1.
  • 2.
  • 3.

我们必须创建一个then的重载,它接受一个能返回 promise 的函数。为了能够串联调用then,这个方法必须也返回一个promise。这个then的原型如下。

func then<NewValue>(onResolved: @escaping (Value) -> Promise<NewValue>) -> Promise<NewValue> { 
    // to implement 

  • 1.
  • 2.
  • 3.

注:细心的读者可能已经发现,我们在给Promise实现flatMap。就如给Optional和Array定义flatMap一样,我们也可以给Promise定义它。

困难来了。让我们一步步看看这个“flatMap”的then要怎么实现。

  • 我们需要返回一个Promise
  • 谁给我们这样一个 promise?onResolved 方法
  • 但onResolved 需要一个类型为Value的 value 为参数。我们该怎样得到这个 value? 我们可以使用之前定义的then(或者说 “observe”) 来在其可用时访问它

如果写成代码,大概如下:

func then<NewValue>(onResolved: @escaping (Value) -> Promise<NewValue>) -> Promise<NewValue> { 
    then { value in // the "observe" one 
        let promise = onResolved(value) // `promise` is a Promise<NewValue> 
        // problem: how do we return `promise` to the outside ?? 
    } 
    return // ??!! 

  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.

就快好了。但我们还有个小问题需要修复:这个promise变量被传递给then的闭包所限制。我们不能将其作为函数的返回值。

我们要使用的技巧是创建一个包装Promise,它将执行我们目前所写的代码,然后在promise变量解决时被同时解决。换句话说,当onResolved方法提供的 promise 被解决并从外部得到一个值,那包装的 promise 也就被解决并得到同样的值。

可能文字有些抽象,但如果我们写成代码,将看得更清楚:

func then<NewValue>(onResolved: @escaping (Value) -> Promise<NewValue>) -> Promise<NewValue> { 
    // We have to return a promise, so let's return a new one 
    return Promise<NewValue> { resolve in 
        // this is called immediately as seen in test 0. 
        then { value in // the "observe" one 
            let promise = onResolved(value) // `promise` is a Promise<NewValue> 
            // `promise` has the same type of the Promise wrapper 
            // we can make the wrapper resolves when the `promise` resolves 
            // and gets a value 
            promise.then { value in resolve(value) } 
        } 
    } 

  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.

如果我们整理一下代码,我们将有这样两个方法:

// observe 
func then(onResolved: @escaping (Value) -> Void) { 
    callbacks.append(onResolved) 
    triggerCallbacksIfResolved() 

 
// flatMap 
func then<NewValue>(onResolved: @escaping (Value) -> Promise<NewValue>) -> Promise<NewValue> { 
    return Promise<NewValue> { resolve in 
        then { value in 
            onResolved(value).then(onResolved: resolve) 
        } 
    } 

  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.

最后,测试通过。

• Test 3. Resolution handlers can be chained passed (1 assertions) 
  • 1.

串联多个 value

如果你能给某个类型实现flatMap,那你就能利用flatMap为其实现map。对于我们的Promise来说,map该是什么样子?

我们将使用如下测试:

test(named: "4. Chaining works with non promise return values") { assert, done in 
    let string: String = "foo" 
    let promise = Promise<String> { resolve in 
        after(0.1) { 
            resolve(string) 
        } 
    } 
    promise 
        .then { value -> String in 
            return value + value 
        } 
        .then { value in // the "observe" then 
            assert(string + string == value) 
            done() 
        } 

  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.

注意第一个then没有再返回一个Promise,而是将其接收的值做了一个变换。这个新的then就对应于我们想添加的map。

编译器报错说我们必须实现此方法。

error: Promise.playground:174:26: error: declared closure result 'String' is incompatible with contextual type 'Void' 
        .then { value -> String in 
                         ^~~~~~ 
                         Void 
  • 1.
  • 2.
  • 3.
  • 4.

这个方法很接近flatMap,唯一的不同是其参数onResolved函数返回一个NewValue而不是Promise

// map 
func then<NewValue>(onResolved: @escaping (Value) -> NewValue) -> Promise<NewValue> { 
    // to implement 

  • 1.
  • 2.
  • 3.
  • 4.

之前我们说可以利用flatMap实现map。在我们的情况里,我们看到我们需要返回一个Promise。如果我们使用这个“flatMap”的then,并创建一个promise,再以映射后的 value 来直接解决,我们就搞定了。让我来证明之。

// map 
func then<NewValue>(onResolved: @escaping (Value) -> NewValue) -> Promise<NewValue> { 
    return then { value in // the "flatMap" defined before 
        // must return a Promise<NewValue> here 
        // this promise directly resolves with the mapped value 
        return Promise<NewValue> { resolve in 
            let newValue = onResolved(value) 
            resolve(newValue) 
        } 
    } 

  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.

再一次,测试通过。

• Test 4. Chaining works with non promise return values passed (1 assertions) 
  • 1.

如果我们移除注释,再看看我们做出了什么。我们有三个then方法的实现,能被使用或串联。

// observe 
func then(onResolved: @escaping (Value) -> Void) { 
    callbacks.append(onResolved) 
    triggerCallbacksIfResolved() 

 
// flatMap 
func then<NewValue>(onResolved: @escaping (Value) -> Promise<NewValue>) -> Promise<NewValue> { 
    return Promise<NewValue> { resolve in 
        then { value in 
            onResolved(value).then(onResolved: resolve) 
        } 
    } 

 
// map 
func then<NewValue>(onResolved: @escaping (Value) -> NewValue) -> Promise<NewValue> { 
    return then { value in 
        return Promise<NewValue> { resolve in 
            resolve(onResolved(value)) 
        } 
    } 

  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.
  • 20.
  • 21.
  • 22.
  • 23.

使用示例

实现告一段落。我们的Promise类已足够完整来展示我们能够用它做什么。

假设我们的app有一些用户,结构如下:

struct User { 
    let id: Int 
    let name: String 

  • 1.
  • 2.
  • 3.
  • 4.

假设我们还有两个方法,一个获取用户id列表,另一个使用id获取某个用户。而且假设我们想显示第一个用户的名字。

通过我们的实现,我们可以这样做,使用之前定义的这三个then。

func fetchIds() -> Promise<[Int]> { 
    ... 

 
func fetchUser(id: Int) -> Promise<User> { 
    ... 

 
fetchIds() 
    .then { ids in // flatMap 
        return fetchUser(id: ids[0]) 
    } 
    .then { user in // map 
        return user.name 
    } 
    .then { name in // observe 
        print(name
    } 
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.

代码变得十分易读、简洁,而且没有嵌套。

结论本文结束,希望你喜欢它。

参考资料

[1]gist:

https://gist.github.com/felginep/039ca3b21e4f0cabb1c06126d9164680

[2]Promises in Swift by Khanlou:

http://khanlou.com/2016/08/promises-in-swift/

[3]JavaScript Promises … In Wicked Detail:

https://www.mattgreer.org/articles/promises-in-wicked-detail/

[4]PromiseKit 6 Release Details:

https://promisekit.org/news/2018/02/PromiseKit-6.0-Released/

[5]TDD Implementation of Promises in JavaScript:

https://www.youtube.com/watch?v=C3kUMPtt4hY

[6]Implementing Promises in Swift:

https://felginep.github.io/2019-01-06/implementing-promises-in-swift

本文转载自微信公众号「Swift社区」

 

责任编辑:姜华 来源: Swift社区
相关推荐

2022-12-20 07:39:46

2023-11-20 08:18:49

Netty服务器

2023-12-21 17:11:21

Containerd管理工具命令行

2021-05-29 10:11:00

Kafa数据业务

2023-07-31 08:18:50

Docker参数容器

2022-11-11 19:09:13

架构

2023-11-06 08:16:19

APM系统运维

2022-07-18 21:53:46

RocketMQ广播消息

2024-10-10 09:12:10

Spring接口初始化

2023-11-08 08:15:48

服务监控Zipkin

2023-10-27 08:15:45

2022-02-24 07:34:10

SSL协议加密

2024-05-22 09:45:49

2023-03-06 21:29:41

mmap技术操作系统

2022-05-16 10:49:28

网络协议数据

2019-06-13 21:31:19

AI

2020-11-27 09:40:53

Rollup前端代码

2022-04-08 09:01:14

CSS自定义属性前端

2021-09-13 22:34:56

区块链新基建数字化转型

2022-02-28 12:07:56

RxJS函数式
点赞
收藏

51CTO技术栈公众号