使用 Vapor 迭代调用方法 returns a Future
Using Vapor to iteratively call a method the returns a Future
我正在使用 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
returns nextCursor
的值和已获取页面的用户列表。我需要继续使用 nextCursor
的值调用此方法,并为每个页面累积用户,直到 nextCursor
returns -1
。我将如何使用从此方法返回的 Future
迭代调用它,直到我访问了游标的所有页面,同时累积每次调用返回的 User
s?
这是我目前所拥有的,但我不知所措。我觉得我离题太远了。
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
中你可以创建一个私有的 _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([=10=].users)
if [=10=].nextCursor > 0 {
return try self._followersFetcher(of: screenName, nextCursor: [=10=].nextCursor, users: newUsers).map {[=10=]}
}
return self.eventLoop.future(UserCursor(users: newUsers.map{[=10=]}))
}
}
func fetchFollwers(of screenName : String) throws -> Future<[User]> {
return try _followersFetcher(of: screenName).map{[=10=].users}
}
}
对于 Vapor 和 NIO,始终保持事件循环非常重要。在上面的示例中,_followersFetcher
根据需要多次调用自身以获取所有用户,然后才 returns 结果。
您可以重写代码,使它看起来更漂亮 clean/elegant,但我认为这是唯一可用的技术,用于仅在查询上一个光标后才获得下一个光标的情况。
如果你事先有一个游标列表,你可以简单地使用 flatten
private func _followersFetcher(of screenName : String, cursors: [Int64]) throws -> Future<[User]> {
var users: Set<User> = []
return cursors.map {
_followers(of: screenName, nextCursor: [=11=]).map {
users.union([=11=].users)
}
}.flatten(on: eventLoop).map { users.map { [=11=] } }
}
我正在使用 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
returns nextCursor
的值和已获取页面的用户列表。我需要继续使用 nextCursor
的值调用此方法,并为每个页面累积用户,直到 nextCursor
returns -1
。我将如何使用从此方法返回的 Future
迭代调用它,直到我访问了游标的所有页面,同时累积每次调用返回的 User
s?
这是我目前所拥有的,但我不知所措。我觉得我离题太远了。
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
中你可以创建一个私有的 _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([=10=].users)
if [=10=].nextCursor > 0 {
return try self._followersFetcher(of: screenName, nextCursor: [=10=].nextCursor, users: newUsers).map {[=10=]}
}
return self.eventLoop.future(UserCursor(users: newUsers.map{[=10=]}))
}
}
func fetchFollwers(of screenName : String) throws -> Future<[User]> {
return try _followersFetcher(of: screenName).map{[=10=].users}
}
}
对于 Vapor 和 NIO,始终保持事件循环非常重要。在上面的示例中,_followersFetcher
根据需要多次调用自身以获取所有用户,然后才 returns 结果。
您可以重写代码,使它看起来更漂亮 clean/elegant,但我认为这是唯一可用的技术,用于仅在查询上一个光标后才获得下一个光标的情况。
如果你事先有一个游标列表,你可以简单地使用 flatten
private func _followersFetcher(of screenName : String, cursors: [Int64]) throws -> Future<[User]> {
var users: Set<User> = []
return cursors.map {
_followers(of: screenName, nextCursor: [=11=]).map {
users.union([=11=].users)
}
}.flatten(on: eventLoop).map { users.map { [=11=] } }
}