使用Vapor反复调用一个返回Future的方法。

问题描述 投票:2回答:1

我使用的是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的每次呼叫返回?

这是我目前掌握的情况,但我很茫然。我觉得我的方法不对。

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)
    }
}
swift vapor
1个回答
2
投票

我认为在 twitter 你可以私下 _followersFetcher 方法,该方法将调用 _followers 直至 -1 游标,以及公共 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 根据需要多次调用自己来获取所有用户,然后才返回结果。

你可以重写代码,也许会让它看起来更简洁,但我认为这是唯一的技术,当你在查询完上一个游标后才得到下一个游标。

如果你事先有一个游标列表,你可以简单的使用 flatten

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 } }
}
© www.soinside.com 2019 - 2024. All rights reserved.