法罗承诺:

Pharo promises:

今年早些时候,我在 Pharo Smalltalk 参与了一个 promises 项目。 这个想法是为了实现以下行为:

([ 30 seconds wait. 4 ]promiseValue )then: [ :a| Transcript crShow: a ].

这意味着 promise 将在后台等待 30 秒并打印在 Transcript 上。这不应导致 Pharo 用户界面冻结。 我下面的实现冻结了用户界面。为什么?

Class 实现承诺行为的承诺:

Object subclass: #Promise
    instanceVariableNames: 'promiseValue promiseError promiseLock'
    classVariableNames: ''
    package: 'META-Project-[pgakuo]'

里面的方法classPromise

doesNotUnderstand: aMessage 
    ^ self value 
        perform: aMessage selector
        withArguments: aMessage arguments

then: aBlock
    promiseLock isSignaled
        ifTrue: [ ^ self ].
    promiseLock wait.
    promiseError
        ifNotNil: [ promiseError
                privHandlerContext: thisContext;
                signal ].
    aBlock value: promiseValue.
    self value: aBlock

then: aBlock catch: anotherBlock
    promiseLock isSignaled
        ifFalse:
            [ promiseLock wait.
            promiseError ifNotNil: [ anotherBlock value: promiseError ].
            promiseValue ifNotNil: [  aBlock value: promiseValue. self value: aBlock  ]] 

value
    promiseLock  isSignaled ifFalse: [ promiseLock  wait ].
    promiseError  ifNotNil: 
        [ promiseError 
            privHandlerContext: thisContext;
            signal ].
    ^promiseValue 

value: aBlock 
    promiseLock := Semaphore new.
[
  [[promiseValue := aBlock value] 
    on: Error do: [:err | promiseError := err]]
    ensure: [promiseLock signal]] fork

并向 Blockclosure 添加了一种方法,使闭包使用 Promise 行为。

promiseValue
    ^ Promise  new value: self 

一个块被传递给一个Promise的实例,它被Promise>>value:执行,它使用fork在后台执行任务。但它似乎没有按预期工作

在游乐场工作时,您将在 UI 流程中工作。因此,您实际上是在用您的示例暂停 UI 进程。试试这个:

[ ([ 30 seconds wait. 4 ] promiseValue) then: [ :a |
    Transcript crShow: a ] ] forkAt: Processor userBackgroundPriority.

编辑

由于原始表达式明确要求不锁定 UI,您应该做的是:

  1. 不要覆盖 #doesNotUnderstand:
  2. 你有一个选择:

    1. 评估承诺时总是分叉

      由于进程调度和进程创建,这将产生开销。您还将丢失原始进程的上下文,除非您明确保存它(消耗内存,导致性能损失)

    2. 仅当当前进程是UI进程

      时才fork

      判断当前进程是否为UI进程简单快捷。这不是您通常会做的事情,但对于您的情况,我推荐这种方法。

  3. 我建议为 Promise 实施 class 辅助方法,例如Promise class>>value:。这将使您能够将此特定案例与其余实施隔离开来。例如

    value: aBlock
    | instance |
    instance := self new.
    self isUIProcess
        ifTrue: [ [ instance value: aBlock ] forkAt: Processor userBackgroundPriority ]
        ifFalse: [ instance value: aBlock ].
    ^ instance
    

我解决的卡顿问题如下:

then: aBlock
    promiseLock isSignaled
        ifFalse:
            [ promiseLock wait.         
            promiseValue ifNotNil: [  aBlock value: promiseValue ]] fork. 

并且操场上的以下代码不会冻结 UI

[ 12 seconds wait. 12 ]promiseValue then: [ :a|  Transcript crShow: a/2 ] 

它在 12 秒未冻结后打印 6 UI