Вопрос был:
Как вернуть ответ от асинхронного вызова?
, который может быть интерпретирован как:
Как сделать синхронный асинхронный код синхронным?
Решение будет состоять в том, чтобы избежать обратных вызовов и использовать комбинацию Promises и async / await.
Я хотел бы привести пример для запроса Ajax.
(Хотя он может быть записан в Javascript, я предпочитаю писать его на Python и компилировать его в Javascript, используя Transcrypt . Это будет достаточно ясно.)
Позволяет сначала включить использование JQuery, чтобы
$
был доступен какS
:__pragma__ ('alias', 'S', '$')
Определить функцию, которая возвращает Promise, в этом случае вызов Ajax:
def read(url: str): deferred = S.Deferred() S.ajax({'type': "POST", 'url': url, 'data': { }, 'success': lambda d: deferred.resolve(d), 'error': lambda e: deferred.reject(e) }) return deferred.promise()
Использовать асинхронный код, как если бы он был синхронным:
async def readALot(): try: result1 = await read("url_1") result2 = await read("url_2") except Exception: console.warn("Reading a lot failed")
Вы не можете сделать это с помощью маршрутов, потому что в системе маршрутизации нет информации, необходимой для принятия этого решения. Все Rails на этой точке запроса знают, что параметры имеют и не имеют доступа к чему-либо в базе данных.
Что вам нужно, это метод контроллера, который может загружать любые данные, по-видимому, пользователь записывать и перенаправлять, используя redirect_to
.
Это довольно стандартная вещь.
Обновление:
Чтобы выполнить все это в одном вы должны разделить свою логику в соответствии с ролью. Например:
class HomeController < ApplicationController
def home
case
when @user.student?
student_home
when @user.admin?
admin_home
when @user.instructor
instructor_home
else
# Unknown user type? Render error or use a default.
end
end
protected
def instructor_home
# ...
render(:template => 'instructor_home')
end
def student_home
# ...
render(:template => 'student_home')
end
def admin_home
# ...
render(:template => 'admin_home')
end
end
Я предоставляю альтернативный подход, так как этот вопрос SO поднимается вверху при поиске ролевой маршрутизации в Rails.
Недавно мне нужно было реализовать что-то подобное, но мне хотелось избежать большого количества условностей в контроллере - это усугублялось тем фактом, что каждая из моих пользовательских ролей требовала загрузки и представления совершенно разных данных. Я решил переместить решающую логику на уровень маршрутизации, используя Ограничение маршрутизации .
# app/constraints/role_route_constraint.rb
class RoleRouteConstraint
def initialize(&block)
@block = block || lambda { |user| true }
end
def matches?(request)
user = current_user(request)
user.present? && @block.call(user)
end
def current_user(request)
User.find_by_id(request.session[:user_id])
end
end
Самой важной частью вышеуказанного кода является метод matches?
, который будет определить, будет ли маршрут соответствовать. Метод передается объекту request
, который содержит различную информацию о выполненном запросе. В моем случае я просматриваю :user_id
, хранящийся в cookie сессии, и используя это, чтобы найти пользователя, делающего запрос.
Затем вы можете использовать это ограничение при определении своих маршрутов.
# config/routes.rb
Rails.application.routes.draw do
get 'home', to: 'administrators#home', constraints: RoleRouteConstraint.new { |user| user.admin? }
get 'home', to: 'instructors#home', constraints: RoleRouteConstraint.new { |user| user.instructor? }
get 'home', to: 'students#home', constraints: RoleRouteConstraint.new { |user| user.student? }
end
С помощью вышеизложенного администратор, делающий запрос на /home
, будет перенаправлен на home действие AdministratorsController
, инструктор делает запрос к /home
будет перенаправлен на home действие InstructorsController
, а учащийся, сделавший запрос к /home
, будет перенаправлен на home действие StudentsController
.
Если вы ищете дополнительную информацию, я недавно написал об этом подходе в моем блоге .