Conjuring JQuery Deferred with monadic incantations

前端 未结 3 947
名媛妹妹
名媛妹妹 2021-01-13 06:34

Inspired by this (excellent) discussion of using Promises in javascript, I\'m trying to work out how I could use Deferred to chain together async and non-async functions, to

相关标签:
3条回答
  • 2021-01-13 06:44

    I think the way that you'd turn a value into a Promise is to just "pre-succeed" the Deferred:

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

    Now passing a function to ".done()" will result in the function being immediately called with the value.

    v2p("hello").done(function(value) { alert(value); });
    

    would immediately alert "hello".

    0 讨论(0)
  • 2021-01-13 06:57

    Helped along by @Pointy, implementing 'lift' becomes trivial:

    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
    

    Now I just have to work out how to implement [a] -> (a -> M<b>) -> M<[b]>, and what to call it!

    EDIT, I ended up implementing (a -> M<b>) -> ([a] -> M<[b]>) instead, it looks like this:

    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();
        };
    }
    

    So, given a function which gets one deferred item, this function listBind returns a new function which accepts an array of values, and uses them to return another list of values inside a deferred item.

    0 讨论(0)
  • 2021-01-13 06:58

    Wrapping a value into a promise is as simple as using $.when:

    var promise = $.when( value );
    

    Also, as of jQuery 1.6, you have a very simple chaining method (pipe):

    var chained = functionThatReturnsAPromise().pipe(function( resolveValue ) {
            return functionThatReturnsAnotherPromise( resolveValue );
        });
    
    chained.done(function() {
        // Both asynchronous operations done in sequence
    });
    

    Hope this helps.

    0 讨论(0)
提交回复
热议问题