Play 2.5.x (Scala) Как поместить значение, полученное через wsClient, в (ленивый) val

Вариант использования на самом деле довольно типичен. Многие веб-службы используют токены авторизации, которые вы получаете в начале сеанса, и вам необходимо отправлять их обратно при последующих запросах.

Я знаю, что могу сделать это так:

lazy val myData = {    
    val request = ws.url("/some/url").withAuth(user, password, WSAuthScheme.BASIC).withHeaders("Accept" -> "application/json")
    Await.result(request.get().map{x => x.json }, 120.seconds)
}

Это просто кажется неправильным, так как все документы говорят, что мы никогда не ждем.

Есть ли способ справиться с этим в стиле Future/Promise Scala?

Я нашел .onComplete, который позволяет мне запускать код после завершения обещания, однако без использования (изменяемого) var я не вижу способа получить значение в этой области в lazy val в другой области. Даже с var возможна проблема с синхронизацией - отсюда и зло изменяемых переменных :)

Любой другой способ сделать это?


person Techmag    schedule 03.06.2016    source источник
comment
Если ваша цель (не относящаяся конкретно к PlayWS) состоит в том, чтобы получить T из Future[T], объявление ее как lazy val для меня ничего не меняет: либо вы используете Await (с известным недостатком), либо не можете.   -  person cchantep    schedule 03.06.2016
comment
Шаблон собирает эти данные один раз и использует их много раз - именно то, что подразумевает lazy val. Я пытаюсь перевернуть вещи (сначала в своей голове), чтобы, как только это значение будет получено, я собрал все остальное, что мне нужно, в области .onComplete и закончил обработку там. Учитывая, что нужно сделать и где это нужно сделать (внутри Актера), этот подход может помочь.   -  person Techmag    schedule 04.06.2016
comment
Вы можете иметь Future[T] как lazy val, но не уверен, что это действительно полезно   -  person cchantep    schedule 04.06.2016
comment
К вашему сведению: приведенный выше код вызывает какой-то ад внутри Актера, который я еще не смог расшифровать. Если я вызову этот код через действие контроллера, используя Action.sync {...}, если проблем не будет. Однако, если я попытаюсь вызвать точно такой же код в триггере Актера с помощью задачи таймера, тогда код просто перестанет работать. Я не получаю блокировку, я просто получаю код, который падает с обрыва при первом вызове ws.url(). Таймеры продолжают работать, как и сам сайт, и все же Актер, кажется, никогда не делает реальных веб-вызовов. Пока не придумали, как это обойти...   -  person Techmag    schedule 05.06.2016
comment
Сотрите этот последний комментарий -- stackoverflow.com/a/17612613/2162886 -- по-видимому, @Inject на уровне поля не не работает, если ваш класс сам не введен. Это простительно, но не кричать, черт возьми, когда это не удается. У меня был совершенно тихий нулевой указатель - без трассировки стека - НИЧЕГО. Дни снова потрачены впустую из-за пролитого Guice...   -  person Techmag    schedule 05.06.2016
comment
Последняя головная боль в этом постоянном потоке Play/Akka/Guice заключается в том, что теперь, когда у меня это работает в режиме dev, оно тихо выходит из строя в режиме testProd по совершенно неизвестным причинам. Переместили этот вопрос в группы Google.   -  person Techmag    schedule 05.06.2016


Ответы (1)


К сожалению, нет способа сделать это неблокирующим — блоки lazy val спроектированы так, чтобы быть синхронными и блокировать любой поток, обращающийся к ним, до тех пор, пока они не будут заполнены значением (внутри блок lazy val представлен как простой блок synchronized).

Способ Future/Promise Scala состоит в том, чтобы использовать Future[T] или Promise[T] вместо val x: T, но этот способ подразумевает большие накладные расходы с executionContexts и maps при каждом использовании val, и более оптимальное использование ресурсов может не стоить снижения читабельности. во всех случаях, поэтому можно оставить Await там, если вы широко используете это значение во многих частях вашего приложения.

person Sergey    schedule 03.06.2016