I am confused with how to write decent code when using a lot of asynchronous code.
In the following code snippet I log in to get the authentication cookie and use that cookie for the next request to get a list of projects name (as an example):
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
While this will work the code feels dirty. Not really following the single responsibility principle. I would like to extract this in a few methods:
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
This obviously does not work. The getProjects
method is called before doLogin
is finished and the projects are only known in the scope of the block, not giving back the data to the populateProjectsTable
method.
How does one program such applications without the nesting shown in the first example?
You're not going to totally get away from the nesting. Taking Alan's answer and massaging it a bit, this is what I've come up with. It involves passing a block through a couple of methods.
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