4

我正在尝试轮询分页 API 并在用户出现时向用户提供新项目。

fun connect(): Sequence<T> = buildSequence {
    while (true) {
        // result is a List<T>
        val result = dataSource.getFirstPage()
        yieldAll(/* the new data in `result` */)

        // Block the thread for a little bit
    }
}

这是示例用法:

for (item in connect()) {
    // do something as each item is made available
}

我的第一个想法是使用该delay功能,但我收到以下消息:

受限挂起函数只能在其受限协程范围内调用成员或扩展挂起函数

这是签名buildSequence

public fun <T> buildSequence(builderAction: suspend SequenceBuilder<T>.() -> Unit): Sequence<T>

我认为这条消息意味着我只能使用suspendSequenceBuilder: 中的函数,yield并且不允许yieldAll使用任意函数调用。suspend

现在我正在使用它来阻止每次轮询 API 后一秒钟的序列构建:

val resumeTime = System.nanoTime() + TimeUnit.SECONDS.toNanos(1)
while (resumeTime > System.nanoTime()) {
    // do nothing
}

这行得通,但它似乎真的不是一个好的解决方案。有没有人遇到过这个问题?

4

1 回答 1

10

为什么它不起作用?一些研究

当我们查看 时buildSequence,我们可以看到它以 abuilderAction: suspend SequenceBuilder<T>.() -> Unit作为参数。作为该方法的客户,您将能够交付一个以接收器为接收器的suspendlambda (在此处SequenceBuilder阅读有关带有接收器的 lambda )。本身带有注释 :
SequenceBuilderRestrictSuspension

@RestrictsSuspension
@SinceKotlin("1.1")
public abstract class SequenceBuilder<in T> ...

注释的定义和注释如下:

/**
 * Classes and interfaces marked with this annotation are restricted
 * when used as receivers for extension `suspend` functions. 
 * These `suspend` extensions can only invoke other member or extension     
 * `suspend` functions on this particular receiver only 
 * and are restricted from calling arbitrary suspension functions.
 */
@SinceKotlin("1.1") @Target(AnnotationTarget.CLASS) @Retention(AnnotationRetention.BINARY)
public annotation class RestrictsSuspension

正如RestrictSuspension文档所述,在 的情况下buildSequence,您可以传递一个 lambdaSequenceBuilder作为其接收器,但可能性有限,因为您只能调用“suspend此特定接收器上的其他成员或扩展函数”。这意味着,传递给的块buildSequence可以调用定义在SequenceBuilder(如yield, yieldAll)上的任何方法。另一方面,由于该块“限制调用任意挂起函数”,因此 usingdelay不起作用。产生的编译器错误验证它:

受限挂起函数只能在其受限协程范围内调用成员或扩展挂起函数。

最终,您需要知道创建的协程是同步buildSequence协程的一个示例。在您的示例中,序列代码将在通过调用使用序列的同一线程中执行。connect()

如何延迟序列?

正如我们所了解的,ThebuildSequence创建了一个同步序列。在这里使用常规线程阻塞很好:

fun connect(): Sequence<T> = buildSequence {
    while (true) {
        val result = dataSource.getFirstPage()
        yieldAll(result)
        Thread.sleep(1000)
    }
}

但是,你真的想要阻塞整个线程吗?或者,您可以按照此处所述实现异步序列。因此,usingdelay和其他挂起函数将是有效的。

于 2018-03-13T19:29:33.663 回答