在使用大量异步代码时,我对如何编写合适的代码感到困惑。
在下面的代码片段中,我登录以获取身份验证cookie并使用该cookie获取下一个请求以获取项目名称列表(作为示例):
def self.populateProjectsTable(projects_controller)
payload = {email: "email", password: "pass"}
HTTP.post("http://example.com/login", {payload: payload}) do |response|
authCookie = response.headers['Set-Cookie']
HTTP.get("http://example.com/projects.json", {cookie: authCookie}) do |response|
projects = JSON.parse(response.body.to_str)
projects_controller.projects = projects
projects_controller.reloadData
end
end
end
虽然这会起作用,但代码感觉很脏。没有真正遵循单一责任原则。我想用几种方法提取这个:
def self.populateProjectsTable(projects_controller)
@taskList = TaskList.new
@taskList.doLogin
projects = @taskList.getProjects
projects_controller.projects = projects
projects_controller.reloadData
end
def doLogin
payload = {email: "email", password: "pass"}
HTTP.post("http://example.com/login", {payload: payload}) do |response|
@authCookie = response.headers['Set-Cookie']
end
end
def getProjects
HTTP.get("http://example.com/projects.json", {cookie: @authCookie}) do |response|
projects = JSON.parse(response.body.to_str)
end
end
这显然不起作用。在getProjects
完成之前调用doLogin
方法,并且项目仅在块的范围内已知,而不是将数据返回给populateProjectsTable
方法。
如何在没有第一个示例中显示的嵌套的情况下编程此类应用程序?
答案 0 :(得分:4)
你不会完全摆脱筑巢。采取艾伦的回答并稍微按摩它,这就是我想出来的。它涉及通过一些方法传递一个块。
def self.populateProjectsTable(projects_controller)
@taskList = TaskList.new
@taskList.loginAndGetProjects do |projects|
projects_controller.projects = projects
projects_controller.reloadData
end
end
def loginAndGetProjects(&block)
payload = {email: "email", password: "pass"}
HTTP.post("http://example.com/login", {payload: payload}) do |response|
@authCookie = response.headers['Set-Cookie']
getProjects(&block)
end
end
def getProjects(&block)
HTTP.get("http://example.com/projects.json", {cookie: @authCookie}) do |response|
projects = JSON.parse(response.body.to_str)
block.call(projects)
end
end
答案 1 :(得分:4)
我有一个类似的问题,试图包装自己占用块的方法。我希望新的包装器方法仍然可以使用块。这是我在ParseModel中所做的:
# with block:
# ParseModel::Cloud.callFunction("myFunction", {"myParam" => "myValue"}) do |result, error|
# # do something...
# end
# without block:
# ParseModel::Cloud.callFunction("myFunction", {"myParam" => "myValue"})
module ParseModel
class Cloud
def self.callFunction(function, params={}, &block)
return PFCloud.callFunction(function, withParameters:params) unless block_given?
PFCloud.callFunctionInBackground(function, withParameters:params, block:lambda do |result, error|
block.call(result, error)
end)
end
end
end
将此概念应用于您的问题,您可以重写您的方法以自行获取块。这里有一些我认为可能有用的重构:
def self.populateProjectsTable(projects_controller)
@taskList = TaskList.new
@taskList.doLogin do |login_response|
authCookie = login_response.headers['Set-Cookie']
@taskList.getProjects(authCookie) do |projects_response|
projects = JSON.parse(projects_response.body.to_str)
projects_controller.projects = projects
projects_controller.reloadData
end
end
end
def doLogin(&block)
payload = {email: "email", password: "pass"}
HTTP.post("http://example.com/login", {payload: payload}) do |response|
block.call(response)
end
end
def getProjects(cookie, &block)
HTTP.get("http://example.com/projects.json", {cookie: cookie}) do |response|
block.call(response)
end
end
我不认为你对SRP完全不了解,但这应该是一个好的开始。
答案 2 :(得分:1)
为Jamon的回答+1。
如果您喜欢SRP,我可能会建议使用类来管理您的会话并将API拆分为模块。在添加其他API调用时,这尤其有用。在这里,我排队一旦登录完成就会满足的请求。稍后您可以添加超时处理等。
module ProjectApi
def get_projects(&block)
with_session do
HTTP.get("http://example.com/projects.json", {cookie: @auth_cookie}) do |response|
projects = JSON.parse(response.body.to_str)
block.call(projects)
end
end
end
end
class MySession
include ProjectApi
def initialize(login, password)
@login = login
@password = password
@state = nil
@requests = []
end
def active?
@state == :active
end
def with_session(&block)
@requests << &block
active? ? handle_requests : login(true)
end
private
def login(do_handle_requests = false)
payload = {login: @login, password: @password}
@state = nil
HTTP.post("http://example.com/login", {payload: payload}) do |response|
@state = :active
@auth_cookie = response.headers['Set-Cookie']}
handle_requests if do_handle_requests
end
end
def handle_requests
while request = @requests.shift do
request.call
end if active?
end
end
def self.populateProjectsTable(projects_controller)
@session ||= MySession.new('mylogin', 'mypassword')
@session.get_projects do |projects|
projects_controller.projects = projects
projects_controller.reloadData
end
end