Lazyと非同期処理

やばい、おもしろいなあ。こういうのはできれば休日にしてほしいよ。

http://la.ma.la/blog/diary_200702210356.htm

lazy化するところも汎用的に書いたらだめかしらん?

Function.prototype.to_lazy = function() {
  var orig = this;
  var args = Array.prototype.slice.apply(arguments);
  var lazy_object = {
    isLazy: true,
    force: function(callback){
      orig.receive_lazy().apply(
        null, 
        args.concat(function(value){
          lazy_object.isLazy = false;
          lazy_object.value = value;
          callback(lazy_object.value);
        })
      );
    }
  };
  return lazy_object;
}

lazy化できる関数は、最後の引数にcallback関数をとるものとする。

ためしにdel.icio.usのネットワークをとってくるコード。JSONPなので、基本的にはすべて非同期。
同期の関数は、こちらを参考に非同期化している。
http://d.hatena.ne.jp/amachang/20061129/1164799871

// minimal JSONP
Jsonp = {
  count : 0,
  callbacks : {},
  invoke : function(url, callback) {
    var count = this.count++;
    var s = document.createElement('script');
    s.type = 'text/javascript';
    s.charset = 'utf-8';
    s.src = url + (url.indexOf('?') > 0 ? '&' : '?')
                + 'callback=Jsonp.callbacks._'+count;
    this.callbacks['_'+count] = function() {
      delete Jsonp.callbacks['_'+count];
      callback.apply(null, arguments)
    }
    document.body.appendChild(s);
  }
}


function readDelId(callback) {
  $('delGetButton').onclick = function() {
    $('delGetButton').disabled = true;
    callback($('delId').value)
  }
}

function getDelNetwork(username, callback) {
  Jsonp.invoke(
    'http://del.icio.us/feeds/json/network/'+username, 
    callback
  )
}

function getDelPosts(username, callback) {
  Jsonp.invoke(
    'http://del.icio.us/feeds/json/'+username, 
    function(posts) {
      callback({ username : username, posts : posts })
    }
  )
};

function map_async(arr, func, cont) {
  var count = arr.length;
  var rarr = new Array(arr.length);
  if (count==0) cont(rarr);
  arr.map(function(a, i) {
    func(arr[i], function(ret) {
      rarr[i] = ret;
      count--;
      if (count==0) cont(rarr);
    })
  })
}

function render(networkPosts, cont) {
  $('result').innerHTML = '<div>'+
    networkPosts.map(function(np) {
      return '<h2>'+np.username+'</h2>'+
        np.posts.map(function(post) {
          return '<a href="'+post.u+'">'+post.d+'</a>';
        }).join('<br/>')
    }).join('</div><div>') + '</div>';

  $('delGetButton').disabled = false;

  cont();
}


//
function getNetworkPosts() {
  var id = readDelId.to_lazy();
  var networks = getDelNetwork.to_lazy(id);
  var networkPosts = map_async.to_lazy(
    Array.slice.asynchronize().to_lazy(networks, 0, 5), 
    getDelPosts
  );
  return render.to_lazy(networkPosts);
}

window.onload = function() {
  getNetworkPosts().force(arguments.callee);
}


実際に動いているコードはこちらで。
http://www.geocities.jp/stormriders999/lazy_delicious.html