2016-09-11 3 views
2

Я переношу код из синхронного в асинхронный. Проблема довольно проста: вызовите ряд функций и остановитесь с первым, чтобы вернуть результат без ошибок (возвращая это значение, в противном случае - последнее вычисленное значение). Я начал с:Еще одна scala Futures composition puzzler

def find(fs: Seq[Function0[Int]], result: Int = -1): Int = { 
    if (fs.isEmpty) result 
    else { 
    val res = fs.head() 
    if (res != 0) res 
    else find(fs.tail, res) 
    } 
} 

Однако, если функции становятся асинхронной (т.е. вернуть будущее [Int]) Я не могу получить правильный вызов. Например,

def ffind(ffs: Seq[Function0[Future[Int]]], result: Future[Int] = Future { -1 }): Future[Int] = { 
    if (ffs.isEmpty) result 
    else ffind(ffs.tail, ffs.head()) 
} 

отлично работает, но оценивает все функции независимо от возвращаемого значения. Но что-то вроде:

def findBad(ffs: Seq[Function0[Future[Int]]], result: Future[Int] = Future { -1 }): Future[Int] = { 
    if (ffs.isEmpty) result 
    else { 
     ffs.head() map { res => 
     if (res != 0) res 
     else findBad(ffs.tail, Future(res)) 
     } 
    } 
} 

не входит. Какие-либо предложения? Мы можем предположить, что каждый вызов функции дорог, поэтому никто не должен вызываться дважды, ни после первого «успешного» вызова в последовательности. TIA

+0

привет, это разрешено? – slouc

+0

да, извините, мне не удалось обновить вопрос, просто подтвердив ответ. Очень признателен! –

+0

Я просто убираю, потому что я обсессивно-компульсивный, поэтому мне интересно, разрешил ли мой ответ вашу проблему, и в этом случае вы должны отметить ее как принятую, или ее было недостаточно, и в этом случае я ее удалю. – slouc

ответ

1

Вот почему это не тип-чек: findBad возвращает Future[Int], но отображение res в вызове findBad приведет к Future[Future[Int]]. Вы должны изменить map на flatMap. Обратите внимание, что теперь вам также необходимо обернуть res из первого условия (если res! = 0) в Future, чтобы обе ветви вернули Future. Вот код:

ffs.head() flatMap { res => 
    if (res != 0) Future.succesful(res) 
    else findBad(ffs.tail, Future(res)) 
} 

BTW, если вы хотите запустить их все и вернуться в зависимости от того завершает первый, игнорируя все остальное, то это немного другая проблема (см here), но вы говорите, что каждый вызов функции дорого поэтому я сомневаюсь, что это то, что вы пытаетесь сделать.

+0

Никогда не используйте 'Future.apply' для констант, для чего предназначен' Future.successful'. «Будущее (res)» создаст дополнительную ненужную работу, поскольку она будет назначена и выполнена, как будто результат неизвестен. В сигнатуре есть даже «неявный ex: ExecutionContext». – flavian

+0

Я думал об этом точно так же, как я писал, но в этом случае я считаю, что разница на практике тривиальна, поэтому я не стал редактировать. Но ОК, сделанный пункт, изменится. – slouc