使用单子式咒语召唤JQuery Deferred

5

受到this(优秀)关于在JavaScript中使用Promises的讨论的启发,我正在尝试找出如何使用Deferred将异步和非异步函数链接在一起,以避免在使用我的“全局存储”代码时支付回调费用。

我有几个与此相关的问题,但我会在这里一起提问,因为上下文是相同的。

其中一件我无法解决的事情是如何使不是异步的东西成为延迟对象 - 也就是说,如何将一个值包装在Promise中并直接返回它?(a -> M<a>)

此外,我如何将异步函数包装起来,以便它直接返回其结果,但是包装在Promise中呢?((a -> b) -> (a -> M<b>))

最后一个问题,对于单子爱好者,这个函数有标准名称吗?[a] -> (a -> M<b>) -> M<[b]>

对于任何想要了解如何“链接”延迟对象的人,我提出了(在我看来)优雅的解决方案:http://jsfiddle.net/Benjol/DjrRD/ - Benjol
3个回答

5
将一个值封装成Promise很简单,只需要使用$.when:
var promise = $.when( value );

此外,自jQuery 1.6版本以来,您可以使用非常简单的链式调用方法(管道):
var chained = functionThatReturnsAPromise().pipe(function( resolveValue ) {
        return functionThatReturnsAnotherPromise( resolveValue );
    });

chained.done(function() {
    // Both asynchronous operations done in sequence
});

希望这能帮到你。

非常好,谢谢。恰好我正在调整它适用于这里的情况,他们还没有升级到1.6版(据我所知),但$ .when很方便。 - Benjol
对于任何好奇的人,$.when(value)$.Deferred().resolve(value).promise()的简写。 - chaz

1

我认为将值转换为Promise的方法就是“预先成功”Deferred:

function v2p(value) {
  var rv = $.Deferred();
  rv.resolveWith(null, [value]);
  return rv.promise();
}

现在将函数传递给“.done()”将导致该函数立即使用该值被调用。
v2p("hello").done(function(value) { alert(value); });

会立即提示“你好”。


1
太好了,这是个不错的开始,谢谢!我编辑了你的答案以使其运行,希望没有改变你的意思。 - Benjol
哦,是的,对不起。我只是稍微玩了一下那个Deferred(延迟)东西,但它看起来真的很酷:-) - Pointy

1

在 @Pointy 的帮助下,实现“lift”变得微不足道:

function unit(value) {
  var rv = $.Deferred();
  rv.resolveWith(null, [value]);
  return rv.promise();
}
function lift(fn) {
  return function(x) {
    return unit(fn(x));
  };
}
lift(alert)("hello");

function bind(fn) {
   return function(x) {
     return x.done(function(y) { return fn(y); });
   }
}

function compose(f, g) { return function(x) { g(f(x)); } };
function twice(x) { return 2 * x; }
var alert2 = compose(bind(lift(twice)), bind(lift(alert)));

alert2(unit(4)); //error at the end because alert doesn't return any values

现在我只需要想办法实现 [a] -> (a -> M<b>) -> M<[b]>,然后想个名字就行了!

编辑,最终我实现了 (a -> M<b>) -> ([a] -> M<[b]>),它看起来像这样:

function listBind(fn) {
    return function(a) {        
      var Mb = $.Deferred();
      var b = [], pending = a.length;
      var callback = function(i,val) {
         b[i] = val;
         if(--pending == 0) Mb.resolve(b);
      };
      for(var i = 0, n = a.length; i < n; i++) {
          (function(closure) { //ugly, but have to use closure to 'copy' i
             fn(a[closure]).done(function(val) { callback(closure, val); })
          })(i);
      }
      return Mb.promise();
    };
}

所以,假设有一个函数可以获取一个延迟项目,这个函数listBind返回一个新的函数,该函数接受一个值数组,并使用它们在延迟项目内返回另一个值列表。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接