Учитывая три задачи: FeedCat()
, SellHouse()
и BuyCar()
, есть два интересных случая: либо они все завершаются синхронно (по какой-то причине, возможно, кэширование или ошибка), либо они не делают.
Скажем, у нас есть вопрос:
Task<string> DoTheThings() {
Task<Cat> x = FeedCat();
Task<House> y = FeedCat();
Task<Tesla> z = FeedCat();
// what here?
}
. Теперь простой подход будет:
Task.WhenAll(x, y, z);
, но ... это не удобный для обработки результатов; мы обычно хотели бы await
, что:
async Task<string> DoTheThings() {
Task<Cat> x = FeedCat();
Task<House> y = FeedCat();
Task<Tesla> z = FeedCat();
await Task.WhenAll(x, y, z);
// presumably we want to do something with the results...
return DoWhatever(x.Result, y.Result, z.Result);
}
, но это делает много накладных расходов и выделяет различные массивы (включая массив params Task[]
) и списки (внутренне). Это работает, но это не очень хорошая ИМО. Во многих отношениях проще использовать операцию async
и только await
каждый по очереди:
async Task<string> DoTheThings() {
Task<Cat> x = FeedCat();
Task<House> y = FeedCat();
Task<Tesla> z = FeedCat();
// do something with the results...
return DoWhatever(await x, await y, await z);
}
Вопреки некоторым комментариям выше, использование await
вместо Task.WhenAll
делает не отличается от того, как выполняются задачи (одновременно, последовательно и т. д.). На самом высоком уровне Task.WhenAll
предшествует хорошей поддержке компилятора для async
/ await
и полезен , когда эти вещи не существовали . Это также полезно, когда у вас есть произвольный массив задач, а не 3 сдержанных задач.
Но: у нас все еще есть проблема, что async
/ await
генерирует много шума компилятора для продолжения , Если вероятность того, что задачи действительно завершится синхронно, , мы можем оптимизировать это, построив синхронный путь с асинхронным резервным копированием:
Task<string> DoTheThings() {
Task<Cat> x = FeedCat();
Task<House> y = FeedCat();
Task<Tesla> z = FeedCat();
if(x.Status == TaskStatus.RanToCompletion &&
y.Status == TaskStatus.RanToCompletion &&
z.Status == TaskStatus.RanToCompletion)
return Task.FromResult(
DoWhatever(a.Result, b.Result, c.Result));
// we can safely access .Result, as they are known
// to be ran-to-completion
return Awaited(x, y, z);
}
async Task Awaited(Task<Cat> a, Task<House> b, Task<Tesla> c) {
return DoWhatever(await x, await y, await z);
}
Этот «путь синхронизации с async fallback "становится все более распространенным, особенно в высокопроизводительном коде, где синхронные завершения относительно часты. Обратите внимание, что это вообще не поможет, если завершение всегда истинно асинхронно.
Дополнительные действия, которые применяются здесь:
1: с недавним C #, общий шаблон для async
метод резервного копирования обычно реализуется как локальная функция:
Task<string> DoTheThings() {
async Task<string> Awaited(Task<Cat> a, Task<House> b, Task<Tesla> c) {
return DoWhatever(await a, await b, await c);
}
Task<Cat> x = FeedCat();
Task<House> y = FeedCat();
Task<Tesla> z = FeedCat();
if(x.Status == TaskStatus.RanToCompletion &&
y.Status == TaskStatus.RanToCompletion &&
z.Status == TaskStatus.RanToCompletion)
return Task.FromResult(
DoWhatever(a.Result, b.Result, c.Result));
// we can safely access .Result, as they are known
// to be ran-to-completion
return Awaited(x, y, z);
}
2: предпочитает ValueTask<T>
- Task<T>
, если есть хорошие шансы на вещи когда-либо полностью синхронно со многими различными значениями возврата:
ValueTask<string> DoTheThings() {
async ValueTask<string> Awaited(ValueTask<Cat> a, Task<House> b, Task<Tesla> c) {
return DoWhatever(await a, await b, await c);
}
ValueTask<Cat> x = FeedCat();
ValueTask<House> y = FeedCat();
ValueTask<Tesla> z = FeedCat();
if(x.IsCompletedSuccessfully &&
y.IsCompletedSuccessfully &&
z.IsCompletedSuccessfully)
return new ValueTask<string>(
DoWhatever(a.Result, b.Result, c.Result));
// we can safely access .Result, as they are known
// to be ran-to-completion
return Awaited(x, y, z);
}
3: если возможно, предпочитайте IsCompletedSuccessfully
- Status == TaskStatus.RanToCompletion
; это теперь существует в .NET Core для Task
и везде для ValueTask<T>
Ваша конкретная проблема вызвана тем, что JSF <h:form>
отправляет по умолчанию текущий URL-адрес запроса без какой-либо строки запроса. Посмотрите ближе на сгенерированный вывод HTML, вы увидите
<form action="/app/agreement.xhtml" ...>
. Таким образом, вам явно нужно будет включать эти параметры запроса самостоятельно. Существует несколько способов решить эту проблему. Если вы не отправляли перенаправление, вы можете просто добавить их в виде скрытых входов в форму JSF.
<h:form>
<input type="hidden" name="site" value="#{param.site}" />
<input type="hidden" name="site" value="#{param.serviceId}" />
...
</h:form>
Только эти параметры не появятся в URL-адресе в адресной строке браузера. Это не проблема, если вы используете только использование ajax на той же странице. <h:inputHidden>
, кстати, не подходит, поскольку он смутно потеряет свое значение, когда в форме появляется ошибка преобразования или проверки.
Чтобы заставить их снова появиться в URL-адресе, вам нужно <f:viewParam>
и includeViewParams
. Чтобы работать includeViewParams
, вам нужно объявить следующее как на исходной странице agreement.xhtml
...
<f:metadata>
<f:viewParam name="site" value="#{agreement.site}" />
<f:viewParam name="serviceId" value="#{agreement.serviceId}" />
</f:metadata>
... и целевой странице generated.xhtml
:
<f:metadata>
<f:viewParam name="site" value="#{generated.site}" />
<f:viewParam name="serviceId" value="#{generated.serviceId}" />
</f:metadata>
Теперь вы можете отправить перенаправление, включая параметры представления, следующим образом:
public String generateMethod() {
// ...
return "generated?faces-redirect=true&includeViewParams=true";
}
Обратите внимание, что bean должен быть @ViewScoped
, чтобы поддерживать эти параметры между открытием страницу с формой и отправкой формы, а также ошибок проверки. В противном случае, если вы придерживаетесь компонента @RequestScoped
, вы должны сохранить их как <f:param>
в компонентах команды:
<h:commandButton ...>
<f:param name="site" value="#{generated.site}" />
<f:param name="serviceId" value="#{generated.serviceId}" />
</h:commandButton>
Невозможно установить их для <f:ajax>
внутренних компонентов ввода, ваш bean должен быть действительно @ViewScoped
.
В качестве альтернативы, если вы уже используете библиотеку утилиты JSF OmniFaces , то вы также можете просто заменить <h:form>
на <o:form>
следующим образом (см. также пример демонстрации ):
<o:form includeRequestParams="true">
Это в основном все. Это приведет к генерации <form action>
с включенной текущей строкой запроса.
<form action="/app/agreement.xhtml?site=US&serviceId=AABBCC" ...>
Эти параметры запроса затем доступны только на карте параметров запроса формы submit. Вам не нужны дополнительные метаданные / viewparams, и вам также не нужно отправлять перенаправление, и ваш bean-компонент может быть сохранен @RequestScoped
, если это необходимо.
public String generateMethod() {
// ...
return "generated";
}
Или, если вы используете библиотеку «симпатичного URL», такую как PrettyFaces или FacesViews или, возможно, что-то домашнее, и намерены отправить точно так же, как и в адресной строке браузера, тогда вы можете использовать useRequestURI
.
<o:form useRequestURI="true">
Ваш generateMethod
должен был бы возвратить
return "generated?site=US&serviceId=AABBCC&faces-redirect=true";
Вы даже можете заменить &
на &
, но избежите его в своем xhtml.
В вашем generated.xhtml
вы можете поймать параметры, которые передаются с помощью <f:viewParam>
, как это
<f:metadata>
<f:viewParam name="site" value="#{yourBean.site}"/><!--Make sure you have a setter-->
<f:viewParam name="serviceId" value="#{yourBean.serviceId}"/><!--Make sure you have a setter-
</f:metadata>
<h:head>
h:input
s,h:message
и другие элементы внутри оригиналаh:form
работать после переключения наo:form
? Или нужна ли дополнительная работа? Исходный commandButton (типsubmit
, используя POST) может также возвратитьvoid/null
тоже? – BBerry 25 May 2016 в 10:33