Swiftでasync/awaitな書き方もできるPromiseライブラリHydra

Promiseは非同期処理を直列実行したい時に問題となるコールバック地獄を解決したり、並列実行したそれぞれの処理の終了タイミングを制御することのできる非同期処理のデザインパターンの1つです。

SwiftではPromiseKitやSwiftTaskが有名ですね。

また、最近ではRxなどのReactive系ライブラリを使ってPromise処理をすることが多いと思います。

今回はasync/awaitライクなこともできるPromiseライブラリのHydraの使い方や解説をします。

# Hydraとは

[Hydra](https://github.com/malcommac/Hydra)

<img src="https://qiita-image-store.s3.amazonaws.com/0/27785/822cc06c-5a9b-771e-4d5c-1c68712eddf6.png" width="400" height="388">

[SwiftDate](https://github.com/malcommac/SwiftDate)や[SwiftLocation](https://github.com/malcommac/SwiftLocation)の作者である[malcommac](https://github.com/malcommac)さんが作成しています


# Hydraの使い方

まずは、Hydraの使い方を簡単に紹介します。

使い方は`Promise`というオブジェクトを生成して、様々なオペレーターをチェーンしていきます。
Promiseを知っている方なら下記のサンプルコードは特に違和感ないと思います。

```swift

Promise { resolve, reject in

    //何かしらの非同期処理などを書く
    //処理完了後にresolveもしくはrejectを叩く
    resolve("hachinobu")

}.then { result in
    //resolveならここ
    print(result) //hachinobu
}.catch { error in
    //rejectならここ
}

```

では、HydraのPromiseオブジェクトや各オペレーターについて解説していきます。

## Promiseオブジェクト

### Promiseの前提知識

まず、前提知識としてPromiseは、`保留中(pending)`,`解決(resolve)`,`拒否(reject)`の3つの状態を持ちます。
そして、その状態に紐づく値が存在します。

| 状態 | 保持する値の型 |
|:-----------:|:------------:|
| 保留中(pending)      |        なし |
| 解決(resolve)    |      Value (ジェネリック型) |
| 拒否(reject)     |        Error |


`保留中(pending)`は、まだPromiseでラップした処理が実行されていないことを表しており、この状態に紐づく値は存在しません。
`解決(resolve)`はジェネリック型によりPromiseオブジェクトの生成時に型が決まります。
`拒否(reject)`は`Error`プロトコルに準拠したエラー情報の値を保持します。


**原則として、Promiseの状態遷移が発生するのは`保留中(pending)`状態の時のみであり、1度、`解決(resolve)`もしくは`拒否(reject)`に状態遷移した後は変わることはありません。**

Promiseを使うということは、状態遷移の連鎖に紐づく処理を書くということです。

### Promiseオブジェクトの生成
Promiseオブジェクトの生成は、イニシャライザの引数となるクロージャに非同期処理を書きます。
その非同期処理の結果に応じて `解決` or `拒否`の状態を決めるクロージャを実行します。

```swift

//画像取得の非同期処理をラップしたPromiseオブジェクト
let fetchImagePromise = Promise<UIImage> { resolve, reject in
    //画像を取得する非同期処理
    Session.send(request) { result in　
        switch result {
        case .success(let image):
            resolve(image)
        case .failure(let error):
            reject(error)
        }
    }
}

```

上記は、`解決`状態の時に`UIImage`型の値を保持するPromiseオブジェクトを生成しています。

イニシャライザで書いた非同期処理では、その処理の結果を経て`resolve`もしくは`reject`クロージャを実行する必要があります。

`resolve`には、そのPromiseが`解決`状態の場合に保持する型(UIImage)の値を引数として渡し、`reject`には`拒否`状態の場合に保持する`Error`情報を渡します。

Promiseにラップされた処理が実行され、**`resolve`もしくは`reject`のクロージャが実行されると、そのPromiseオブジェクトの状態遷移が発生し、状態と値が確定します。**

ちなみにPromiseオブジェクトのイニシャライザの全容は

```swift
public init(in context: Context? = nil, _ body: @escaping Body)
```

となっており、第一引数の`Context`型はHydra独自の列挙型であり、これは、第二引数の`Body`クロージャを実行するスレッドの種類を指定できます。
スレッドの種類は`main`,`background`,`userInteractive`,`userInitiated`,`utility`,`custom(queue: DispatchQueue)`と優先度も含めDispatchQueueで用意されているものと同じものが定義されています。

第二引数の`Body`型は`resolve`、`reject`クロージャを引数に取り、主に非同期処理をするクロージャです。
(サンプルでは画像取得の非同期処理を書いた部分です)

また、デフォルト引数により`context`には`nil`が代入されるので、第一引数は指定せず、

```swift
let fetchImagePromise = Promise<UIImage> { resolve, reject in
    //bodyクロージャの処理
}
```

というような書き方が可能です。

`context`を指定しなかった場合は、第二引数の`body`クロージャはメインスレッドで実行されます。

下記は`Context`型を指定して第二引数の`body`クロージャを実行するスレッドを明示的にしている例です。

```swift
Promise(in: .background) { resolve, reject in
    //Backgroundスレッドで実行される処理
}
```

ちなみにイニシャライザで書いた非同期処理は、Promiseオブジェクトを生成しただけでは、まだ実行されていません。
生成したPromiseオブジェクトに対して、次から説明する`オペレーター`をチェーンすることで処理が走ります。


また、これまでで使用した下記のイニシャライザで生成したPromiseオブジェクトの状態は`pending`です。

```swift
public init(in context: Context? = nil, _ body: @escaping Body)
```

これとは別に、`解決(resolve)`や`拒否(reject)`状態のPromiseを生成するイニシャライザもあります。

```swift
public init(resolved value: Value)
public init(rejected error: Error)
```


#### 注意
本記事ではPromiseの`解決`状態、`拒否`状態という単語を使ってこれ以降の説明をしています。

もしかすると、厳密な意味合いでは少しズレますが、
`解決` = 処理の成功
`拒否` = 処理の失敗
と読み替えたほうが直感的で分かりやすいかもしれません。

Promiseでラップした処理が成功した場合は、そのPromiseを`解決`状態に、Promiseでラップした処理が失敗した場合は、`拒否`状態にするというのが一般的な使い方だと思いますので。

## オペレーター

Promiseオブジェクトを生成しただけでは、まだ何も起きません。
Promiseでラップした処理を実行させ、Promiseの状態遷移に応じて行いたい処理があるはずです。
ここからは、Promiseの状態遷移に応じて使えるHydraのオペレーターを紹介します。

各オペレーターはPromiseオブジェクトのインスタンスメソッド、またはグローバル関数で定義されています。

### then

```swift
public func then(in context: Context? = nil, _ body: @escaping ( (Value) throws -> () ) ) -> Promise<Value>

public func then<N>(in context: Context? = nil, _ body: @escaping ( (Value) throws -> N) ) -> Promise<N>

public func then<N>(in context: Context? = nil, _ body: @escaping ( (Value) throws -> (Promise<N>) )) -> Promise<N>
```

`then`は、チェーン元のPromiseが`解決`状態の場合に実行する処理を書きます。
(チェーン元のPromiseの処理で`resolve`が実行された場合)

[Promiseオブジェクトの生成](http://qiita.com/hachinobu/items/fb7b3e52a7b9ee430496#promise%E3%82%AA%E3%83%96%E3%82%B8%E3%82%A7%E3%82%AF%E3%83%88%E3%81%AE%E7%94%9F%E6%88%90)のサンプルコードで使用した`Promise<UIImage>`の変数である[fetchImagePromise](http://qiita.com/hachinobu/items/fb7b3e52a7b9ee430496#promise%E3%82%AA%E3%83%96%E3%82%B8%E3%82%A7%E3%82%AF%E3%83%88%E3%81%AE%E7%94%9F%E6%88%90)で画像取得処理が成功し、`resolve(image)`が実行されると、チェーン元のPromiseの値`image`が引数として`then`の処理が実行されます。

下記は、画像取得のAPIを叩いて取得できたら、UIを更新するサンプルです。

```swift
//fetchImagePromiseは画像を取得する非同期処理をラップしたPromise
fetchImagePromise.then { image in
   // 画像取得できた場合
   self.myImageView.image = image
}
```

`Promise<UIImage>`である`fetchImagePromise`が`解決`状態になった場合に`then`の処理が実行されます。

また、Promiseオブジェクト同様、`then`の第一引数は`Context?`となっていて、デフォルト引数は`nil`です。
指定しなかった場合、`then`オペレーターは、第二引数の`body`クロージャをメインスレッドで実行します。
なので、UIの更新処理をする場合でも、下記のように明示的にメインスレッドを指定する必要はありません。

```swift
.then(in: .main) { image in
    //UI更新処理
}
```

なお、`then`オペレーターは3種類あり、上記のサンプル以外にも`body`クロージャの中で、`解決`状態時にチェーン元とは違う型の値を保持するPromiseオブジェクト`Promise<N>`を返すパターンと、新たにオブジェクト`N`を返す`then`が存在します。

この辺りは後述する[Promiseチェーン](http://qiita.com/hachinobu/items/fb7b3e52a7b9ee430496#promise%E3%83%81%E3%82%A7%E3%83%BC%E3%83%B3)の説明で使います。

### catch

```swift
public func `catch`(in context: Context? = nil, _ body: @escaping ((Error) throws -> (Void))) -> Promise<Void>
```

`catch`は、チェーン元のPromiseが`拒否`状態の場合に実行する処理を書きます。
(チェーン元のPromiseの処理で`reject`が実行された場合)


下記は、画像取得が失敗した場合にエラーをハンドリングするサンプルです。

```swift
fetchImagePromise.catch { error in
    //Errorを使った処理
}
```

`Promise<UIImage>`である`fetchImagePromise`が`拒否`状態になった場合に`catch`の処理が実行されます。
引数には`reject(error)`で指定した`error`が渡ってきます。

ちなみに`then`オペレータ同様、`catch`の第一引数`Context?`を指定しなかった場合は、`catch`の処理はメインスレッドで実行されます。

### Promiseチェーン

Promiseは`解決`と`拒否`の状態を持つので、これまでのサンプルのように`then`のみ`catch`のみチェーンする場合はあまり無いと思います。

`解決`と`拒否`状態の両方に対応する場合`then`に対して`catch`もチェーンで繋ぎます。


下記は画像取得が成功した場合と失敗した場合に対応するサンプルコードです。

```swift
fetchImagePromise.then { image in
    //fetchImagePromiseが`解決`状態(画像取得成功時)の処理
    self.myImageView.image = image
}.catch { error in
    //fetchImagePromiseが`拒否`状態(画像取得失敗時)の処理
}
```

この場合、画像取得の非同期処理をラップしたPromiseの状態が`解決`になった場合は`then`の処理が実行され、`拒否`になった場合は`catch`の処理が実行されます。
`then`や`catch`オペレーターの返り値がPromiseオブジェクトであることから、こういった書き方が可能なのです。


また、`then`と`catch`の処理が両方とも呼ばれることはありません。
仮に非同期処理をラップしたPromiseを下記のように書いても両方呼ばれることはありません。

```swift
Promise { resolve, reject in
    //何かしらの非同期処理
    //処理が完了して、resolveとrejectを2つ呼ぶ
    resolve(result)
    reject(error)
}.then { result in
    //解決状態の処理
}.catch { error in
    //拒否状態の処理
}
```

この場合は、大元のPromiseの処理で先に`resolve`を実行しているので、`then`の処理のみ呼ばれます。

これは、Promiseオブジェクトの説明で記載した通り、原則としてPromiseの状態遷移は`保留中(pending)`からの1度のみになっているためです。
なので`resolve`の後に`reject`を実行しても、そのPromiseの状態は`解決`のまま変わりません。

ちなみに`then`よりも先に`catch`でチェーンした場合は`then`に渡ってくる引数の値は、大元のPromiseの処理が成功して`解決`状態になっていたとしても`Void`です。

```swift
Promise<String> { resolve, reject in
    //何かしらの非同期処理 resultはString型
    resolve(result)
}.catch { error in
    //拒否
}.then { result in
    //解決
    //resultはString型ではなく、Void型になる
}
```

なぜかというと、`catch`の返り値が`Promise<Void>`になっているので、`Promise<Void>`に`then`をチェーンしているためです。
`then`のチェーン元`catch`で返されるPromiseは`解決`状態の時に`Void`を保持するPromiseだからです。

このことから分かるのは、大元のPromiseが`解決`状態になったとしても`catch`オペレーターの処理自体は行われているということです。(当然といえば当然ですが。。)

コードを見た感覚として`catch`が飛ばされているように見えているだけで、単に`catch`オペレーターの引数に書いたクロージャを`catch`の処理内で呼び出していないだけなのです。

このサンプルコードの一連の流れで`大元のPromise`,`catchの返り値のPromise`,`thenの返り値のPromise`という3つのPromiseが生成され、それら全ては`解決`状態のPromiseです。(`then`が呼ばれる場合)

##### Promiseチェーンで直列実行

`then`オペレーターを複数チェーンすることで、ある処理が終わったら次にこの処理をするといった、順番通りに処理を実行する直列実行ができます。

`then`オペレーターにはチェーン元のPromiseが取りうる型とは違う型のPromiseオブジェクトを生成できるインターフェースが用意されているので、柔軟に`解決`状態の取りうる型の違うPromiseを一連のチェーンの中で生成できます。

例えば、ユーザーIDを取得する非同期処理をして成功した場合は、取得したユーザーIDから、そのユーザーのアバター画像を取得するAPIを叩いて、画像が取得できたら画面に表示するといった処理をPromiseの直列実行で書くと下記のようになります。

```swift

Promise { resolve, reject in
    //userIdを取得する非同期処理があるとする
    //成功したのでresolveを実行しPromiseの状態を`解決`、値を"hachinobu"に確定
    resolve("hachinobu")
}.then { userId -> UIImage in
    //チェーン元のPromiseの解決状態の値userId("hachinobu")を使ってアバター取得APIを叩くとする
    let request = Request(userId: userId)
    Session.send(request) { result in　
        switch result {
        case .success(let avatar):
            resolve(image)
        case .failure(let error):
            reject(error)
        }
    }
}.then { avatar in
    //1つめのthenのPromiseが解決状態になると画像が渡ってくる
    self.myImageView.image = avatar
}.catch { error in
    //一連の処理でPromiseが拒否状態になった場合にくる
}

```

まず、1つ目の`then`の処理で、チェーン元のPromiseの値を元に画像を取得する処理を行い、UIImageを返しています。
ここで新たに`Promise<UIImage>(resolve: avatar)`のオブジェクトが生成され、1つ目の`then`の返り値となります。
2つ目の`then`は、`解決状態の時にUIImage型の値を保持するPromise`に対してチェーンしているので、UIImage型の`avatar`が取得できるのです。


### always

```swift
public func always(in context: Context? = nil, body: @escaping () throws -> Void) -> Promise<Value>
```

`always`はチェーン元のPromiseの状態が`解決`,`拒否`のどちらの場合でも実行される処理を書きます。

例えば、通信する際にローディング中のインジケータを出しておいて、`always`でそのインジケータを消すといった、どちらの結果であっても行うべき処理を書きます。

```swift
fetchImagePromise.then { image in
    self.myImageView.image = image
}.catch { error in
    //エラー処理
    }.always(in: .main) {
    //明示的に.mainを指定して、このクロージャをメインスレッドで実行させる
}
```

注意点として`always`は、第一引数の`Context?`を指定しなかった場合、バックグラウンドスレッドで処理を実行するので、UI更新処理を書く場合は`.main`を指定する必要があります。


### defer

```swift
public func `defer`(in context: Context? = nil, _ seconds: TimeInterval) -> Promise<Value>
```

`defer`はチェーン元のPromiseが`解決`状態の場合に、`defer`の後続のオペレーターの処理を`seconds`引数で指定した秒数遅延させることができます。
厳密にいうと`defer`オペレーターで返されるPromiseの状態遷移を`seconds`秒遅延させます。
(`保留中`から`解決`or`拒否`の状態遷移を遅延することで、後続のオペレーターは動作しないため)

下記は画像を取得してから2.0秒後に画面に反映されるサンプルコードです。

```swift
fetchImagePromise.defer(2.0).then { image in
    //画像取得成功(resolve(image))から2.0秒後に呼ばれる
    self.myImageView.image = image
}.catch { error in
    //fetchImagePromiseの状態が拒否の場合はスグに呼ばれる
}
```

fetchImagePromiseで画像の取得に失敗した場合は、遅延は起きません。
`defer`は`拒否`状態のPromiseの場合は、`拒否`状態のPromiseを即座に返すので、`catch`オペレータの処理が実行されます。

### retry

```swift
public func retry(_ attempts: Int = 3, _ condition: @escaping ((Int, Error) throws -> Bool) = { _ in true }) -> Promise<Value>
```

`retry`を使うと、チェーン元のPromiseが`拒否`状態の場合に、チェーン元のPromiseの処理を、第一引数で指定した回数まで再実行させることができます。

第二引数の`condition`は、`後何回実行できるかの回数`と`チェーン元Promiseの拒否状態の値(Error)`を引数に受け、チェーン元のPromiseの処理を再実行させるか判定するクロージャを書きます。


下記は画像取得に失敗しても最大2回は再び画像取得の処理を試みます。

```swift
fetchImagePromise.retry(2) { (count, error) -> Bool in
    //ここでcountとerrorの引数をもとにして再実行するか判定する
    return true
}.then { image in
    self.myImageView.image = image
}.catch { error in
    //エラー処理
}
```

また、`retry`にはデフォルト引数があり、最大再試行回数は`attempts: Int = 3`、再試行するかは`condition: @escaping ((Int, Error) throws -> Bool) = { _ in true }`となっているので

```swift
//画像取得に失敗したら最大3回まで再実行する
fetchImagePromise.retry().then { image in
    self.myImageView.image = image
}.catch { error in
    //エラー処理
}
```

だったり、

```swift
//画像取得に失敗したら最大2回まで再実行する
fetchImagePromise.retry(2).then { image in
    self.myImageView.image = image
}.catch { error in
    //エラー処理
}
```

といった書き方や

```swift
fetchImagePromise.retry { (count, error) in
    //ここで再実行するか判定する
    return true
}.then { image in
    self.myImageView.image = image
}.catch { error in
    //エラー処理
}

```

といった書き方ができます。

(余談として`retry`はチェーン元のPromiseの状態を`拒否`から`保留中`に遷移させることで、これを実現しています。)

### recover

```swift
public func recover(in context: Context? = nil, _ body: @escaping (Error) throws -> Promise<Value>) -> Promise<Value>
```

`recover`はチェーン元のPromiseが`拒否`状態の場合に、その値である`error`を引数に受け取り、新たにチェーン元と同じ型を保持するPromiseオブジェクトを返すリカバリ処理を書く事ができます。

先に書いた`retry`オペレータとの違いは、チェーン元のPromiseが`拒否`状態の場合に`1度だけ行う処理`であるということ、そして実行するリカバリ処理は`チェーン元のPromiseの処理とは関係のない処理ができる`ところです。

つまり、`retry`はチェーン元のPromiseの状態を`拒否`から`解決`にさせようとするアプローチなのに対して、`recover`は`recover`が生成するPromiseオブジェクト自身の状態を`解決`にすべくアプローチが取れます。

例えば、画像取得に失敗した場合にエラーとして終わらせるのでなく、デフォルトの画像を差し込む場合に下記のように`retry`を使うと良いのではないでしょうか。

```swift
//画像取得に失敗し、fetchImagePromiseの状態は拒否とする
fetchImagePromise.recover { error -> Promise<UIImage> in
    //処理は失敗したけどデフォルト画像を差し込ませる
    return Promise(resolve: UIImage(named: "default.png")!)
}.then { image in
    //default.pngが渡ってくる
    self.myImageView.image = image
}.catch { error in
    //fetchImagePromiseの状態は拒否だが、recoverで解決状態のPromiseになっているので、ここは呼ばれない
}
```

`then`の処理は実行されますが、`catch`は実行されません。
fetchImagePromiseは画像取得できず`拒否`状態ですが、`recover`で`解決`状態のPromiseが発行されているので後続のチェーンは`解決`状態のPromiseにチェーンしているためです。

`recover`のクロージャには`error`が渡されるので、そのエラー内容に応じたリカバリ処理を書けます。

ちなみに`recover`の第一引数`Context?`を指定しなかった場合は、バックグラウンドスレッドで`recover`の処理が実行されます。

### validate

```swift
public func validate(in context: Context? = nil, _ validate: @escaping ((Value) throws -> (Bool))) -> Promise<Value>
```

`validate`は、チェーン元のPromiseが`解決`状態の場合に、その値を引数に指定した条件で評価します。
条件を満たしていれば`解決`状態のPromise、満たしていなければ`拒否`状態のPromiseを返します。

例えば、通信処理が成功した場合にUserオブジェクトを取得できるが、そのUserオブジェクトのnameプロパティが`nil`だった場合は失敗(エラーである)とみなしたいといった場合、下記のように書けば要件を満たせます。

```swift

Promise<User> { resolve, reject in
    //User情報を取得する処理
    //成功するとUserオブジェクトが取得できるものとする
    Session.send(request) { result in　
        switch result {
        case .success(let user):
            resolve(user)
        case .failure(let error):
            reject(error)
        }
    }
}.validate { user in
    //userオブジェクトのnameプロパティのnil判定
    return user.name != nil
}.then { user in
    //user.nameがnilでない場合に呼ばれる
}.catch { error in
    //User情報取得失敗 or 取得したUser情報のnameがnil
}

```

`Promise<User>`が`解決`状態の場合、保持している値の`User`オブジェクトの`name`プロパティが`nil`でないか評価し、`nil`で無ければ、`解決`状態の`Promise<User>`を生成し、`nil`だった場合は`拒否`状態のPromiseを`validate`オペレーターは返します。

そもそも最初の`Promise<User>`が`拒否`状態の場合は、`validate`の評価の処理は呼び出されません。
その場合は、`validate`でも`then`の処理でも`拒否`状態のPromiseが生成され`catch`の処理が実行されます。

### timeout

```swift
public func timeout(in context: Context? = nil, timeout: TimeInterval, error: Error? = nil) -> Promise<Value>
```

`timeout`は、チェーン元のPromiseの状態が指定した秒数経過しても`解決`or`拒否`にならない場合に、第三引数で指定したエラーを持つ`拒否`状態のPromiseを発行します。

下記は画像取得処理が10秒経過しても終了しない場合はエラーとして、`catch`の処理が呼ばれるサンプルです。

```swift
fetchImagePromise.timeout(timeout: 10.0).then { image in
    self.myImageView.image = image
}.catch { error in
    //fetchImagePromiseの処理に失敗もしくは10秒経過しても終了しない場合
}
```

`timeout`の第一引数contextと第三引数errorにはデフォルト引数が用意されているので、第二引数のtimeoutだけ指定する書き方ができます。

ちなみに第三引数のerrorを指定しなかった場合、`PromiseError`の`timeout`というエラーを持つ`拒否`状態のPromiseが発行されるので`catch`の処理の引数には`timeout`が渡ります。

`PromiseError`は`Error`プロトコルに準拠したHydra独自のenumです。(timeoutは列挙子)

### pass

```swift
public func pass<A>(in context: Context? = nil, _ body: @escaping (Value) throws -> Promise<A>) -> Promise<Value>
```

`pass`はチェーン元のPromiseが`解決`状態の場合に、その値を元にして独自の処理を行い、処理が正しく処理された場合は、チェーン元のPromiseと同じ状態のPromiseを返します。
`pass`の独自処理で、その処理が失敗した場合は、`拒否`状態のPromiseを返します。

例えば、あるデータをAPIから取得した後に、そのデータの整合性を判定するAPIがあるとして、そこに通信処理をして、結果が正しければ、取得したデータをそのまま後続で処理させるが、結果が誤りという結果になった場合は、Promiseの状態を`拒否`にして、それ以降の処理をさせないといった場合でしょうか。


```swift

//あるデータを取得
Promise { resolve, reject in
    Session.send(request) { result in
        switch result {
        case .success(let data):
            resolve(user)
        case .failure(let error):
            reject(error)
        }
    }
}.pass { data -> Promise<Void> in
    //渡ってきたdataの整合性を確認する処理
    //この処理が正の場合はチェーン元と同じPromiseオブジェクトを返す
　　　　　　　　//誤の場合は、後続のチェーンオペレーターに`拒否`状態のPromiseを返す
    return Promise<Void> { resolve, reject in

        let request = Request(data: data)
        Session.send(request) { result in
            switch result {
            case .success(let _):
                //正の場合
                return Promise(resolved: ())
            case .failure(let error):
                //誤の場合
                return Promise(rejected: error)
            }
        }
    }
    
}.then { data in
    //整合性の取れているdataがくる
}.catch { error in
    //整合性が取れなかった or Dataが取れなかった
}

```

このように一連の処理の中で、ある処理を挟み、その処理が失敗した場合には、後続の処理をさせないといった感じです。

### all

```swift
public func all<L, S: Sequence>(_ promises: S, concurrency: UInt = UInt.max) -> Promise<[L]> where S.Iterator.Element == Promise<L>
```

`all`を使うと非同期処理を並列実行して、その終了のタイミングをハンドリングすることが可能です。
このオペレーターはグローバル関数として定義されています。

引数として、Promiseの配列と、その処理の同時実行数`concurrency`を指定できます。
配列内のPromiseオブジェクトは、`解決`状態の型が全て同じPromiseオブジェクトである必要があります。

`all`は配列内のPromiseの処理を並列実行して、その全てのPromiseが`解決`状態になれば、各Promiseの値を配列として持つPromiseオブジェクトが返されます。
どれか1つでも`拒否`状態になった時点で`拒否`状態のPromiseが返されます。
他のPromiseが`保留中`(`pending`)でも、その処理は待たずに`拒否`状態のPromiseを返します。

例えば、ユーザーIDを指定して、そのユーザーの`いいね数`を取得できるAPIがあるとします。
ユーザーID `hachinobu`,`hoge`,`fuga`の`いいね数`をそれぞれ取得する処理のPromiseを生成し、それらの処理を並列実行して、全て成功した場合に合計の`いいね数`を表示するといったことをやりたい時は下記のように書けます。

```swift

//いいね数を取得するPromiseを生成
func fetchLikeCountPromise(userId: String) -> Promise<Int> {
    return Promise<Int> { resolve, reject in
        let likeCountRequest = LikeCountRequest(userId: userId)
        Session.send(likeCountRequest) { result in
            switch result {
            case .success(let likes):
                resolve(likes)
            case .failure(let error):
                reject(error)
            }
        }
    }
}

//それぞれのユーザーのいいね数を取得するPromiseの配列
let likeCountPromises = ["hachinobu", "hoge", "fuga"].map(fetchLikeCountPromise)

all(likeCountPromises).then { likeCounts in

    //likeCounts = [100, 130, 40]
    //それぞれのPromiseの処理の結果が配列で返る
    let displayLikeSum = likeCounts.reduce(0) { (sum, likeCount) in
        return sum + likeCount
    }.description

    self.myLabel.text = displayLikeSum

}.catch { error in

    //配列のPromise処理が1つでも失敗したら実行される

}

```

`all`の引数で指定した配列内の全Promiseの処理が実行され、全Promiseが`解決`状態になると、`then`が実行されます。
`then`に渡ってくる値は、配列内の各Promiseの`解決`状態の時に取る値の配列です。

`all`の引数に渡した配列内のPromiseが1つでも`拒否`状態の場合は、`then`の処理は実行されず、`catch`が実行されます。

### any

```swift
public func any<L>(in context: Context? = nil, _ promises: [Promise<L>]) -> Promise<L>
```

`any`は配列のPromiseの中で一番最初に状態が確定したPromiseを返します。

`any`も`all`同様に`解決`状態の値が同じ型のPromiseの配列を引数に渡します。
`any`は引数に渡した配列内のPromise処理を並列実行させ、一番最初に状態が確定(`解決` or `拒否`)したPromiseオブジェクトが返ります。

`JS`のPromiseの`race`と同じでしょうか。

下記は複数ユーザーの画像の取得処理を並列実行させ、最初に取得できたユーザーの画像を表示するサンプルです。

```swift

//画像を取得する処理をラップしたPromiseを生成
func fetchAvatarImagePromise(userId: String) -> Promise<UIImage> {
    return Promise<UIImage> { resolve, reject in
        let avatarRequest = AvatarRequest(userId: userId)
        Session.send(avatarRequest) { result in
            switch result {
            case .success(let avatar):
                resolve(avatar)
            case .failure(let error):
                reject(error)
            }
        }
    }
}

//それぞれのユーザーのAvatar画像を取得するPromiseの配列
let avatarPromises = ["hachinobu", "hoge", "fuga"].map(fetchAvatarImagePromise)

any(avatarPromises).then { image in
    //一番最初に返ってきたPromiseオブジェクトが`解決`状態だった場合に、そのユーザーのavatar(UIImage)が渡される
    self.firstAvatarImageView.image = image
}.catch { error in
    //一番最初に返ってきたPromiseオブジェクトが`拒否`状態だった場合に呼ばれる
}

```

このように並列実行して一番早く返ってきた結果に基づいて処理したい場合に使います。
2番目、3番目のPromiseの状態が`解決`もしくは`拒否`だろうと関与しません。

### zip

```swift
public static func zip<A, B>(in context: Context? = nil, _ a: Promise<A>, _ b: Promise<B>) -> Promise<(A,B)>

public static func zip<A,B,C>(in context: Context? = nil, a: Promise<A>, b: Promise<B>, c: Promise<C>) -> Promise<(A,B,C)>

public static func zip<A,B,C,D>(in context: Context? = nil, a: Promise<A>, b: Promise<B>, c: Promise<C>, d: Promise<D>) -> Promise<(A,B,C,D)>
```

`zip`は`all`と同じように複数の非同期処理の終了タイミングなどをハンドリングができます。
`all`のように同じ型の値を取りうるPromiseである必要はありません。

処理を経て、引数に指定したPromiseが全て`解決`状態になると、それぞれの値をタプルで保持した`解決`状態のPromiseが返ります。

`zip`はPromiseクラスの静的関数として定義されています。
現在用意されているインターフェースは`解決`状態の時に取りうる値の型が違うPromiseを最大4つまで指定できます。
引数にはそれぞれ並列実行したいPromiseオブジェクトを渡すだけです。

例えば、ある画面にユーザー情報を表示するために、ユーザー情報のAPIとユーザーのアバターを取得するAPIを叩く必要があるとした場合、APIを叩く処理をそれぞれ並列に実行して、両方が終わったタイミングで取得できた型の違うObjectを元に、画面の表示用に使うViewModelを作るようなケースに使えます。

```swift

//ユーザー情報(オブジェクトUser)を取得する処理のPreomise
let fetchUserPromise = Promise<User> { resolve, reject in
    let request = UserRequest(id: "hachinobu")
    Session.send(request) { result in
        switch result {
        case .success(let user):
            resolve(user)
        case .failure(let error):
            reject(error)
        }
    }
}

//ユーザーのアバター画像(UIImage)を取得する処理のPromise
let fetchAvatarPromise = Promise<UIImage> { resolve, reject in
    let request = AvatarRequest(id: "hachinobu")
    Session.send(request) { result in
        switch result {
        case .success(let avatar):
            resolve(avatar)
        case .failure(let error):
            reject(error)
        }
    }
}

Promise<Void>.zip(fetchUserPromise, fetchAvatarPromise).then { (user, avatar) in
    //fetchUserPromiseとfetchAvatarPromiseの処理が共に成功した場合に呼ばれる
    //引数として各Promiseのresolveの値がタプルで取得できる
    self.userViewModel = UserViewModel(user: user, avatar: avatar)
    self.tableView.reloadView()
}.catch { error in
    //fetchUserPromiseもしくはfetchAvatarPromiseのいずれかが失敗した場合
}

```

### reduce

```swift
public func reduce<A, I, S: Sequence>(in context: Context? = nil, _ items: S, _ initial: I, _ transform: @escaping (I, A) throws -> Promise<I>) -> Promise<I> where S.Iterator.Element == A
```

`reduce`は、Swift言語でお馴染みの`reduce`と同じように、Sequenceの要素をまとめる処理をした結果の値を`解決`状態の時に保持するPromiseを生成することができます。


例えば`all`オペレーターの際に説明した、指定ユーザーの`いいね数`を表示するサンプルコードとして`all`オペレータにチェーンした`then`オペレーターの処理内で、それぞれの`いいね数`の配列([Int])を使って集計しましたが、`all`オペレーターにチェーンした`then`処理内で`reduce`を使って合計値を保持したPromiseオブジェクトを作ることができます。

```swift

//likeCountPromisesはPromise<Int>の配列
all(likeCountPromises).then { likeCounts -> Promise<Int> in

    //likeCounts = [100, 130, 40]
    //reduceを使って各ユーザーの`いいね数`の合計のPromise<Int>を生成
    return reduce(likeCounts, 0) { (totalCount, likeCount) in 
        let total = totalCount + likeCount
        return Promise(resolved: total)
    }

}.then { total in
    //reduceで生成されたPromise<String>のresolveの値が渡ってくる
    self.myLabel.text = total.description
}.catch { error in
    
}

```

## async/await

Hydraは`async/await`が使えます。
これを使えば非同期処理を同期的であるかのように書くことができます。

```swift
public func async(in context: Context, after: TimeInterval? = nil, _ block: @escaping (Void) -> (Void)) -> Void


@discardableResult
public func await<T>(in context: Context = .background, _ body: @escaping ((_ fulfill: @escaping (T) -> (), _ reject: @escaping (Error) -> () ) throws -> ())) throws -> T
```

使い方は、グローバル関数である`async`のクロージャ内で、`await`を使って非同期処理を書きます。
例として画像を取得する非同期処理が成功したら、ImageViewに表示するという一連の流れを`async/await`を用いると下記のように書くことができます。

```swift
async(in: .main) {

    do {
        //awaitを使って非同期処理
        let image: UIImage = try await { resolve, reject in
            Session.send(request) { result in
                switch result {
                case .success(let image):
                    resolve(image)
                case .failure(let error):
                    reject(error)
                }
            }
        }

        //取得できたimageをUIに反映
        self.myImageView.image = image
    } catch {
        //何かしらのエラー処理
    }

}
```

`async`の第一引数には`Context`型を指定し、第二引数のクロージャを実行するスレッドを指定します。
上記サンプルではUIの更新処理をするので`async`に`.main`を指定しています。

`await`で書いた非同期処理が成功すると、その値が返ります。
`await`は非同期処理が終わるまで待ってくれます。
そして、`await`の非同期処理が終わってから、その下の行に書いたUI更新処理`self.myImageView.image = image`が実行されます。

`await`の処理では失敗(reject)になる場合があるので必ず`try`が必要になります。
また、注意点として`await`を使う場合は、ジェネリック関数の型を確定させるために、`await`の処理の返り値を格納する変数に型を明示的に書く必要があります。（型推論はしてくれません。）

`let image: UIImage`

もし変数の型を書かない場合は下記のように`await`の`resolve`クロージャに型を書く必要があります。

```swift
let image = try await { (resolve: @escaping (UIImage) -> (), reject: @escaping (Error) -> ()) in
    Session.send(request) { result in
        switch result {
        case .success(let image):
            resolve(image)
        case .failure(let error):
            reject(error)
        }
    }
}
```

また、`await`にはPromiseオブジェクトを引数に受け取るインターフェースもあります。

```swift
public func await<T>(in context: Context? = nil, _ promise: Promise<T>) throws -> T
```

これを使うとPromiseでラップした非同期処理のインスタンスを用意しておいて`await`の引数に指定して非同期処理を同期的に実行させることができます。

```swift
async(in: .main) {

    do {

        //画像取得の非同期処理をラップしたPromiseオブジェクト
        let fetchImagePromise = Promise<UIImage> { resolve, reject in
            //画像を取得する非同期処理
            Session.send(request) { result in　
                switch result {
                case .success(let image):
                    resolve(image)
                case .failure(let error):
                    reject(error)
                }
            }
        }
        //awaitにPromiseオブジェクトを渡して非同期処理を同期的に実行する
        let image: UIImage = try await(fetchImagePromise)

        //取得できたimageをUIに反映
        self.myImageView.image = image
    } catch {
        //何かしらのエラー処理
    }

}
```

`await`の返り値は、引数で渡したPromiseの`解決`状態の値を取得することができます。
`do-catch`を使うのは、Promiseの`拒否`状態を考慮して、エラーを検知、処理するためです。

先に紹介した

```swift
@discardableResult
public func await<T>(in context: Context = .background, _ body: @escaping ((_ fulfill: @escaping (T) -> (), _ reject: @escaping (Error) -> () ) throws -> ())) throws -> T
```

こちらの`await`でも結局は中でPromiseを生成しています。
使う側がPromiseオブジェクトを意識しないでも書けるインターフェースになっているだけです。

### awaitのシンタックスシュガー

`await`にはシンタックスシュガーが用意されています

```swift
prefix operator ..
public prefix func ..<T> (_ promise: Promise<T>) throws -> T
```

これを`await`の代わりに使えます。
引数としてPromiseオブジェクトを受け取ります。

```swift

async(in: .main) {

    do {
        let fetchImagePromise = Promise<UIImage> { resolve, reject in
            Session.send(request) { result in
                switch result {
                case .success(let image):
                    resolve(image)
                case .failure(let error):
                    reject(error)
                }
            }
        }
        //awaitの代わりに..を使う
        let image: UIImage = try ..(fetchImagePromise)

        //取得できたimageをUIに反映
        self.myImageView.image = image
    } catch {
        //何かしらのエラー処理
    }

}

```

また、`await`を使った時にエラーの内容は特に気にしないのであれば、`await`の代わりに`..!`を使えば`do-catch`を書く必要がなくなります。

```swift
prefix operator ..!
public prefix func ..!<T> (_ promise: Promise<T>) -> T?
```

これを使うと処理が失敗した場合は、`nil`が返ってきます。
`try await`ではなく`try?　await`と書いた場合と同じ挙動になるということです。


```swift

async(in: .main) {

    let fetchImagePromise = Promise<UIImage> { resolve, reject in
        Session.send(request) { result in
            switch result {
            case .success(let image):
                resolve(image)
            case .failure(let error):
                reject(error)
            }
        }
    }
    //..!を使う
    let image: UIImage? = try ..!(fetchImagePromise)
    
    guard let image = image else {
        //何かしらのエラー処理
        return
    }

    //取得できたimageをUIに反映
    self.myImageView.image = image

}

```


### async/awaitでもPromiseチェーン

`async`にはもう１つインターフェースが用意されていて、こちらは返り値としてPromiseオブジェクトを返すのでPromiseチェーンが使えます。

```swift
public func async<T>(in context: Context? = nil, _ body: @escaping ( (Void) throws -> (T)) ) -> Promise<T>
```

```swift
async { _ -> UIImage in

    let image: UIImage = try await { (resolve, reject) in
        Session.send(request) { result in
            switch result {
            case .success(let image):
                resolve(image)
            case .failure(let error):
                reject(error)
            }
        }
    }

    return image

}.then { image in
    //awaitの処理が成功した場合
    self.myImageView.image = image
}.catch { error in
   //awaitの処理が失敗した場合
}
```

こちらを使った場合は`async`が返したPromiseオブジェクトを`then`などのオペレーターでチェーンするまで処理が動きません。(`async`の返り値のPromiseは`保留中`状態ということです。)

先で説明した返り値のない`async`とは混同しないように注意してください。


#### async/awaitで直列実行
`async/await`を使うと非同期処理の直列実行の可読性がPromiseチェーンを用いた場合よりも上がります。

例として、全ユーザー情報を返すAPIを叩いて、Userオブジェクトの配列を取得した後に、先頭のユーザーのIDをもとに、そのユーザーの投稿した記事の一覧、Articleオブジェクトの配列を取得したい場合を想定して書くと下記のように表現できます。

```swift

async (in: .main) {

    var articleList: [Article] = []
    do {
        //ユーザー一覧を取得
        let userList: [User] = try await { resolve, reject in
            Session.send(request) {
                switch result {
                case .success(let userList):
                    resolve(userList)
                case .failure(let error):
                    reject(error)
                }
            }
        }

        //ユーザー一覧配列の先頭のユーザーのidを指定して、そのユーザーの投稿一覧を取得
        let userId = userList.first.id
        articleList = try await {
            //投稿一覧取得のRequest
            let request = ArticleListRequest(userId: userId)
            Session.send(request) {
                switch result {
                case .success(let articleList):
                    resolve(articleList)
                case .failure(let error):
                    reject(error)
                }
            }
            Session.send()
        }

    } catch {
        //エラー処理
        //ユーザー取得もしくは投稿取得のどちらでエラーが発生してもここにくる
        //これ以降の処理をさせない
        return
    }

    //`articleList`を使ったりしてUI更新処理などをする
    self.viewModel = MyViewModel(source: articleList)
    self.tableView.reloadData()

}

```

このように`ユーザー一覧を取得する非同期処理`と`特定のユーザーの投稿一覧を取得する非同期処理`を同期的に書く事ができます。


本来、`await`は`async`の中でしか使えないと思いますが、Hydraでは`await`はグローバル関数で定義されているので、`async`の中で無くても使えてしまいます。

ちなみに`await`は`DispatchSemaphore`のvalueを0にして、処理が終わるまでwaitさせることで実現しています。(処理が完了したらsignalして後続の処理をするといった感じです。)

# 最後に
軽量で高機能なHydraを紹介してみました。
実現方法など、ソースコードの中身まで解説できれば良かったのですが思った以上に長い記事になってしまったので今回はここまでです。
気軽にソースコードを読んでみるだけでも勉強になると思いますので、気になった方は是非読んでみると面白いと思います。