将递归异步函数转换为promise

时间:2017-06-27 19:03:34

标签: swift asynchronous recursion google-drive-api promisekit

我有一个递归的异步函数,使用REST api和完成处理程序查询Google Drive的文件ID:

func queryForFileId(query: GTLRDriveQuery_FilesList,
                    handler: @escaping FileIdCompletionHandler) {
    service.executeQuery(query) { ticket, data, error in
        if let error = error {
            handler(nil, error)
        } else {
            let list = data as! GTLRDrive_FileList 
            if let pageToken = list.nextPageToken {
                query.pageToken = pageToken
                self.queryForFileId(query: query, handler: handler)
            } else if let id = list.files?.first?.identifier {
                handler(id, nil)
            } else {
                handler(nil, nil) // no file found
            }
        }
    }
}

此处,query设置为返回nextPageTokenfiles(id)字段,serviceGTLRDriveService的实例,FileIdCompletionHandler只是一个typealias

typealias FileIdCompletionHandler = (String?, Error?) -> Void

我已经阅读了如何将异步函数转换为promises(如this thread中),但我不知道如何将它应用于递归异步函数。我想我可以将整个方法包装为Promise

private func fileIdPromise(query: GTLRDriveQuery_FilesList) -> Promise<String?> {
    return Promise { fulfill, reject in
        queryForFileId(query: query) { id, error in
            if let error = error {
                reject(error)
            } else {
                fulfill(id)
            }
        }
    }
}

然而,我希望能有更直接的东西:

private func queryForFileId2(query: GTLRDriveQuery_FilesList) -> Promise<String?> {
    return Promise { fulfill, reject in
        service.executeQuery(query) { ticket, data, error in
            if let error = error {
                reject(error)
            } else {
                let list = data as! GTLRDrive_FileList
                if let pageToken = list.nextPageToken {
                    query.pageToken = pageToken
                    // WHAT DO I DO HERE?
                } else if let id = list.files?.first?.identifier {
                    fulfill(id)
                } else {
                    fulfill(nil) // no file found
                }
            }
        }
    }
}

那么:当我需要对executeQuery进行另一次异步调用时,我该怎么做?

1 个答案:

答案 0 :(得分:1)

如果你想要满足递归的承诺,那么你的“我在这做什么?”在这一行中,您将创建一个新的promise.then {...}.else {...}模式,在fulfill子句中调用then,在reject子句中调用else。显然,如果不需要递归调用,你只需直接fulfill

我不知道Google API并且您没有共享您的代码以满足对文件列表的承诺,所以我必须保持这个答案有点通用:让我们假设您有一些{{1返回只有在完成所有文件的所有promises时才满足的promise的例程。让我们想象一下顶级电话是这样的:

retrieveTokens

然后你有一个retrieveTokens(for: files).then { tokens in print(tokens) }.catch { error in print(error) } ,只有当满足各个文件的承诺时,才会返回一个满足的承诺。如果您正在处理一组简单的retrieveTokens对象,您可能会执行以下操作:

File

(我知道这不是你的样子,但是我需要这个框架来展示我对你的问题的答案。但是在一个函数中封装这个“在给定级别返回所有承诺”是有用的,如它允许你保持递归代码有点优雅,而不重复代码。)

然后,返回单个文件的promise的例程将查看是否需要返回一组递归的promise,并将其func retrieveTokens(for files: [File]) -> Promise<[Any]> { var fileGenerator = files.makeIterator() let generator = AnyIterator<Promise<Any>> { guard let file = fileGenerator.next() else { return nil } return self.retrieveToken(for: file) } return when(fulfilled: generator, concurrently: 1) } 放在该新递归创建的promise的fulfill子句中:

then

同样,我知道上述内容并不是您问题的直接答案(因为我不熟悉Google Drive API,也不了解您的顶级承诺逻辑)。因此,在我的示例中,我创建了足以满足the demonstration目的的模型对象。

但希望这足以说明递归承诺背后的想法。