为什么我的效果被召唤两次?

时间:2016-04-29 12:26:31

标签: purescript

我有以下用EffAff s编写的程序。哪个按预期运行。也就是说,它打印出给定的Int并进行异步计算。

type JsonResponse = AffjaxResponse Json
access :: forall e m. Aff (ajax :: AJAX | e) (Either Error JsonResponse)
access = attempt $ get "http://localhost:8080/livesys/Robert"

staging :: forall e. Int -> Eff (console :: CONSOLE | e) Int
staging i = do
    liftEff $ log $ ">>" ++ show i
    return i

main :: forall a. Int -> Aff (ajax :: AJAX, console :: CONSOLE| a) Int
main state = do
    s <- liftEff $ staging state
    a <- liftAff access
    return s

如果我改变了main内的呼叫顺序,那么就会发生一些神秘的事情:

main :: forall a. Int -> Aff (ajax :: AJAX, console :: CONSOLE| a) Int
main state = do
    a <- liftAff access
    s <- liftEff $ staging state
    return s

函数staging现在被称为两次! WUT?

有人可以解释一下吗?

感谢您的帮助

1 个答案:

答案 0 :(得分:0)

可能是一个异常被抛出而不是由Aff实例中的错误函数处理的情况。当与attempt一起使用时,这会导致成功函数的重复调用。

module Main where

import Prelude
import Data.Either (Either(..))
import Control.Monad.Eff (Eff)
import Control.Monad.Eff.Console (log)
import Control.Monad.Eff.Exception (Error, EXCEPTION, throwException, error)
import Control.Monad.Aff (Aff, makeAff, liftEff', launchAff, attempt)

raise = throwException <<< error


myAff :: forall e. Aff e String
myAff = _unsafeInterleaveAff $ makeAff doIt
  where
    doIt _ success = do
      log "operation"
      raise "it's dead jim" 
      success "done"

main = do
  launchAff $ do
    liftEff' $ log "start"
    myAff

foreign import _unsafeInterleaveAff :: forall e1 e2 a. Aff e1 a -> Aff e2 a

此代码会导致doIt被调用两次,但当Aff调用被撤消时则不会。

其他:

虽然这个功能确实有点奇怪。也许用更像这样的attempt来替换它?

exports._attempt = function (Left, Right, aff) {
  return function(success, error) {
    var affCompleted = false;
    try {
      return aff(function(v) {
        affCompleted = true
        success(Right(v));
      }, function(e) {
        affCompleted = true
        success(Left(e));
      });
    } catch (err) {
      if (affCompleted) {
        throw err;
      } else {
        success(Left(err));
      }
    }
  };
}