swift - 使用 Vapor 迭代调用返回 Future 的方法

标签 swift vapor

我正在使用 Swift 5 和 Vapor 3。我编写了一个客户端来调用 Twitter 来获取用户的关注者。看起来像这样

func followersOf(_ screenName : String, nextCursor : Int64 = -1) throws -> Future<UserCursor> {
    logger.debug("Fetching followers of \(screenName)")
    let res = httpClient.get("https://api.twitter.com/1.1/followers/list.json?screen_name=\(screenName)&nextCursor=\(nextCursor)", headers: ["authorization": authToken])
    return res.flatMap { res in
        return try res.content.decode(UserCursor.self, using: self.jsonDecoder)
    }
}

UserCursor 返回 nextCursor 的值以及所获取页面的用户列表。我需要继续使用 nextCursor 的值调用此方法,并累积每个页面的用户,直到 nextCursor 返回 -1。我将如何使用从此方法返回的 Future 来迭代调用它,直到我访问了光标的所有页面,同时累积从每次调用返回的 User ? p>

这是我到目前为止所拥有的,但我不知所措。我觉得我离目标太远了。

func followersOf(_ req : Request) throws -> Future<FollowersView> {
    let logger = try req.make(Logger.self)
    let screenName = try req.parameters.next(String.self)

    logger.debug("Request for followers of \(screenName)")

    let twitter = try req.make(TwitterClient.self)
    return try twitter.followersOf(screenName).flatMap { userCursor in
        var uc = userCursor
        var users : Set<User> = []
        users = users.union(userCursor.users)
        while (uc.nextCursor != -1) {
            try twitter.followersOf(screenName, nextCursor: userCursor.nextCursor).map { uc in uc}
        }
        return FollowersView(screenName, users)
    }
}

最佳答案

我认为在 twitter 中,您可以创建一个私有(private) _followersFetcher 方法,该方法将调用 _followers 直到它得到 -1 > 光标,以及公共(public) fetchFollowers 方法,该方法将与 fetcher 进行处理,如下所示:

import Vapor

class TwitterClient : Service {
    private let authToken : String
    var httpClient : Client

    let jsonDecoder : JSONDecoder
    let logger : Logger
    let eventLoop : EventLoop

    init(_ client : Client, _ logger : Logger) throws {
        jsonDecoder = JSONDecoder()
        jsonDecoder.keyDecodingStrategy = .convertFromSnakeCase

        guard let apiToken = Environment.get("TWITTER_TOKEN") else {
            throw Abort(.internalServerError)
        }

        authToken =  "Bearer " + apiToken

        self.logger = logger

        self.httpClient = client

        self.eventLoop = httpClient.container.eventLoop
    }

    private func _followers(of screenName : String, nextCursor : Int64 = -1) throws -> Future<UserCursor>{
        logger.debug("Fetching followers of \(screenName) cursor \(nextCursor)")
        let res = httpClient.get("https://api.twitter.com/1.1/followers/list.json?screen_name=\(screenName)&cursor=\(nextCursor)", headers: ["authorization": authToken])
        return res.flatMap { res in
            return try res.content.decode(UserCursor.self, using: self.jsonDecoder)
        }
    }

    private func _followersFetcher(of screenName : String, nextCursor : Int64 = -1, users: Set<User> = []) throws -> Future<UserCursor> {
        return try _followers(of: screenName, nextCursor: nextCursor).flatMap {
            let newUsers = users.union($0.users)
            if $0.nextCursor > 0 {
                return try self._followersFetcher(of: screenName, nextCursor: $0.nextCursor, users: newUsers).map {$0}
            }
            return self.eventLoop.future(UserCursor(users: newUsers.map{$0}))
        }
    }

    func fetchFollwers(of screenName : String) throws -> Future<[User]> {
        return try _followersFetcher(of: screenName).map{$0.users}
    }
}

对于 Vapor 和 NIO,始终保持在 eventLoop 上非常重要。在上面的示例中,_followersFetcher 根据需要多次调用自身以获取所有用户,然后才返回结果。

您可能可以重写代码,使其看起来更干净/优雅,但我认为这是当您仅在查询前一个光标后才获取下一个光标的情况下唯一可用的技术。

如果您事先有光标列表,您可以简单地使用展平

private func _followersFetcher(of screenName : String, cursors: [Int64]) throws -> Future<[User]> {
    var users: Set<User> = []
    return cursors.map {
        _followers(of: screenName, nextCursor: $0).map {
            users.union($0.users)
        }
    }.flatten(on: eventLoop).map { users.map { $0 } }
}

关于swift - 使用 Vapor 迭代调用返回 Future 的方法,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/61671408/

相关文章:

swift - 从午睡响应中获取 HTTPURLResponse

ios - swift iOS : Programmatically create a responsive menu using UIButton and a View and constraints

ios - 如何在 Swift 中显示带有 UICollectionView 的弹出 UIViewController?

swift - 使用vapor在Swift中创建REST API无法发出POST请求

swift - Vapor 网络套接字保持与数据库的连接

swift - Ubuntu 上的服务器端 Swift 和 Vapor

swift - Vapor 连接被拒绝(errno 61)

swift - 如何避免uitableview刷新

api - Swift 中的 Youtube API 到 AlamoFire

swift - 在 ubuntu 上使用 MongoSwift 时 Vapor 崩溃