蓝鸟 6.0.0

Bluebird 6.0.0

测试已测试
语言语言 SwiftSwift
许可协议 MIT
发布最后发布2022年8月
SPM支持SPM

Andrew Barba 维护。



Bluebird 6.0.0

  • Andrew Barba

Bluebird.swift

CocoaPods Compatible Carthage Compatible Twitter

Promise/A+ 遵循,由 Bluebird 启发,Swift 5 实现

功能

  • Promise/A+ 遵循
  • Swift 5
  • Promise 取消
  • 性能
  • 轻量级
  • 单元测试
  • 100% 文档化

文档

https://andrewbarba.github.io/Bluebird.swift/

要求

  • iOS 9.0+ / macOS 10.11+ / tvOS 9.0+ / watchOS 2.0+
  • Xcode 10.2
  • Swift 5

安装

Swift包管理器

// swift-tools-version:5.0

import PackageDescription

let package = Package(
    name: "My App",
    dependencies: [
        .package(url: "https://github.com/AndrewBarba/Bluebird.swift.git", from: "5.1.0")
    ]
)

CocoaPods

CocoaPods 1.5.0+ 需要构建 Bluebird

pod 'Bluebird', '~> 5.0'

Carthage

github "AndrewBarba/Bluebird.swift" ~> 5.0

谁在用 Bluebird

在生产中使用 Bluebird?请通过 Pull Request 或 Issue 告诉我。

使用方法

Promise

Promise 是通用的,允许您指定它们最终将解析为的类型。创建 Promise 的首选方法是传递一个闭包,该闭包接受两个函数,一个用于调用以解析 Promise,另一个用于调用以拒绝 Promise

let promise = Promise<Int> { resolve, reject in
  // - resolve(someInt)
  // - reject(someError)
}

resolve 和 reject 函数可以异步或同步调用。这是一个将现有 Cocoa API 包装在自己的代码中以解决 Promise 的好方法。例如,查看一个处理图像的昂贵函数

Promise 之前
func performExpensiveOperation(onImage image: UIImage, completion: @escaping (UIImage?, Error?) -> Void) {
  DispatchQueue(label: "image.operation").async {
    do {
      let image = try ...
      completion(image, nil)
    } catch {
      completion(nil, error)
    }
  }
}
Promise 之后
func performExpensiveOperation(onImage image: UIImage) -> Promise<UIImage> {
  return Promise<UIImage> { resolve, reject in
    DispatchQueue(label: "image.operation").async {
      do {
        let image = try ...
        resolve(image)
      } catch {
        reject(error)
      }
    }
  }
}

所以函数的内体看起来几乎完全相同... 但看看函数签名是多么好看!

不再需要完成处理程序,不再需要可选的图像,不再需要可选的错误。原始函数中的可选项是一个明显的迹象,表明您在未来将需要保护和提取。使用 Promise 实现的这个逻辑被良好的设计所隐藏。现在使用这个新函数是一种享受

let original: UIImage = ...

performExpensiveOperation(onImage: original)
  .then { newImage in
    // do something with the new image
  }
  .catch { error in
    // something went wrong, handle the error
  }

then

您可以使用 then 方法轻松执行一系列操作

authService.login(email: email, password: password)
  .then { auth in userService.read(with: auth) }
  .then { user in favoriteService.list(for: user) }
  .then { favorites in ... }

请注意,每次从 then 处理程序返回一个 Promise(或一个值),下一个 then 处理程序就会接收到该处理程序的解析,等待上一个处理程序完全解析。这对于异步控制流来说非常强大。

Grand Central Dispatch

任何接受处理程序的 Bluebird 方法也接受一个 DispatchQueue,您可以控制要运行处理程序的目标队列

userService.read(id: "123")
  .then(on: backgroundQueue) { user -> UIImage in
    let image = UIImage(user: user)
    ... perform complex image operation ...
    return image
  }
  .then(on: .main) { image in
    self.imageView.image = image
  }

默认情况下,所有处理程序都在 .main 队列上运行。

捕获

使用 catch 来处理/恢复在 Promise 链中发生的错误

authService.login(email: email, password: password)
  .then { auth in userService.read(with: auth) }
  .then { user in favoriteService.list(for: user) }
  .then { favorites in ... }
  .catch { error in
    self.present(error: error)
  }

如上所示,如果任何 then 处理器 throw 出错误,或者处理程序返回的某个 Promise 被拒绝,则将调用最终的 catch 处理器。

您也可以在执行多个异步操作时进行复杂的恢复

Bluebird.try { performFirstOp().catch(handleOpError) }
  .then { performSecondOp().catch(handleOpError) }
  .then { performThirdOp().catch(handleOpError) }
  .then { performFourthOp().catch(handleOpError) }
  .then {
    // all completed
  }

tap

在不需要更改 Promise 类型的情况下,在中途执行操作非常有用

authService.login(email: email, password: password)
  .tap { auth in print(auth) }
  .then { auth in userService.read(with: auth) }
  .tap { user in print(user) }
  .then { user in favoriteService.list(for: user) }
  .then { favorites in ... }

您还可以从 tap 处理器返回一个 Promise,链将等待该 Promise 解决

authService.login(email: email, password: password)
  .then { auth in userService.read(with: auth) }
  .tap { user in userService.updateLastActive(for: user) }
  .then { user in favoriteService.list(for: user) }
  .then { favorites in ... }

finally

使用 finally,您可以在 Promise 链的末尾注册一个处理器,无论其结果如何

spinner.startAnimating()

authService.login(email: email, password: "bad password")
  .then { auth in userService.read(with: auth) } // will not run
  .then { user in favoriteService.list(for: user) } // will not run
  .finally { // this will run!
    spinner.stopAnimating()
  }
  .catch { error in
    // handle error
  }

join

无缝连接不同类型的 Promise

join(fetchArticle(id: "123"), fetchAuthor(id: "456"))
  .then { article, author in
    // ...
  }

map

遍历一系列元素并对每个元素执行一个操作

let articles = ...

map(articles) { article in
  return favoriteService.like(article: article)
}.then { _ in
  // all articles liked successfully
}.catch { error in
  // handle error
}

您还可以使用 mapSeries() 按顺序遍历序列。

reduce

遍历序列并将结果汇总为一个完整解决的 Promise

let users = ...

reduce(users, 0) { partialTime, user in
  return userService.getActiveTime(for: user).then { time in
    return partialTime + time
  }
}.then { totalTime in
  // calculated total time spent in app
}.catch { error in
  // handle error
}

all

等待所有 Promise 完成

all([
  favoriteService.like(article: article1),
  favoriteService.like(article: article2),
  favoriteService.like(article: article3),
  favoriteService.like(article: article4),
]).then { _ in
  // all articles liked
}

any

使用 any 可以轻松处理竞态条件,一旦其中一个 Promise 解决,处理器就会被调用,然后再也不会被调用

let host1 = "https://east.us.com/file"
let host2 = "https://west.us.com/file"

any(download(host1), download(host2))
  .then { data in
    ...
  }

try

启动 Promise 链

// Prefix with Bluebird since try is reserved in Swift
Bluebird.try {
  authService.login(email: email, password: password)
}.then { auth in
  // handle login
}.catch { error in
  // handle error
}

Tests

测试在 Bitrise 上持续运行。由于 Bitrise 不支持公开测试运行,我无法链接到它们,但您可以自己运行测试,方法是打开 Xcode 项目,并从 Bluebird 方案手动运行测试。

Bluebird 与 PromiseKit 的比较

如果我说我没有说过 PromiseKit 是一个非常棒的库(它是!)那就太撒谎了,但 Bluebird 有不同的目标,这可能对不同的开发者有吸引力或没有吸引力。

Xcode 9+/Swift 4+

PromiseKit 不遗余力地保持与 Objective-C、之前版本的 Swift 和之前版本的 Xcode 的兼容性。这是一个庞大的工程,真幸运有他们在做这件事。

泛型与组合

Bluebird在整个库中都有更为复杂的泛型使用,为Swift中的Promise链组合提供了非常好的API。

Bluebird支持与任何Sequence类型一起使用mapreduceallany,而不仅仅是数组。例如,您可以使用Realm中的ListResult类型在这些函数中使用,而PromiseKit不支持这种功能。

Bluebird还支持Promise.mapPromise.reduce(同Bluebird.js),它们的作用与全局等效函数相同,但可以在线上对现有的Promise进行链式操作,这大大增强了Promise的组合能力。

无扩展

PromiseKit提供了许多有用的框架扩展,它们以Promise样式的函数包装核心Cocoa API。目前我没有任何计划提供此类功能,但如果这样做,它将在不同的存储库中,以便我可以保持这个存储库的简洁和严格测试。

Bluebird API 兼容性

在我重用Bluebird.js后,我开始使用PromiseKit,但在使用过程中对API的细微差异和一些实际缺失的功能感到烦恼。Bluebird.swift试图尽可能接近Bluebird.js的API。

Bluebird.js
Promise.resolve(result)
Promise.reject(error)
promise.then(handler)
promise.catch(handler)
promise.finally(() => ...)
promise.tap(value => ...)
Bluebird.swift
Promise(resolve: result)
Promise(reject: error)
promise.then(handler)
promise.catch(handler)
promise.finally { ... }
promise.tap { value in ... }
PromiseKit
Promise(value: result)
Promise(error: error)
promise.then(execute: handler)
promise.catch(execute: handler)
promise.always { ... }
promise.tap { result in
  switch result {
  case .fullfilled(let value):
    ...
  case .rejected(let error):
    ...
  }
}

这些只是其中的一小部分差异,Bluebird.swift 在 Bluebird.js 中确实缺少一些功能,但我的目标是缩小这一差距,并保持一个与应用程序更紧密匹配的 API。