是否有管理javascript同步和异步调用的模式

时间:2013-04-22 23:25:18

标签: javascript ajax asynchronous

我有一个javascript应用程序保存服务器上的所有数据,然后使用REST API通信服务器和客户端。

它们工作正常,直到我们开始有越来越多的嵌套异步调用或嵌套同步调用来隐藏异步调用。例如:

function asyncFoo(callback) {
  callback();
}

function syncCallHidingAsyncCall(){
  syncStuff();
  asyncFoo(function(){
    syncFoo()
  });
}

function nestedAsyncCall(callback){
  asyncFoo(function(){
    anotherAsyncCall(callback);
  })
}
// this make refactor code become so hard.
// if we want add step2() after nestedAsyncCall();
// instead of add one line of code
// we need first add callback param in every asyncCall, then pass step2 as a callback

还有一些不必要的异步调用:

// we actually only verify this once.
function isLogin(callback){
  if (!App._user) {
    ServerApi.getCurUser(function(data){
      App._user = data.user;
      callback(App._user)
    });
  }
  callback(App._user)
}

function syncCallNeedVerfifyLogin(callback){
  // user only need login once, so in most case this is really only a sync call.
  // but now I have to involve a unnecessary callback to get the return value
  isLogin(function(){
    callback(syncStuff())
  })
}

所以在项目变得越来越大之后,我们开始忘记他们的关系,哪一个需要等待,哪一个会做魔术。并且越来越多的功能变得异步,因为一些非常小的东西需要在服务器上进行验证。

所以我开始觉得他们必须在这个项目中遇到一些设计问题。我正在寻找最佳实践或设计模式,或者在这种重型沟通应用程序中需要遵循一些规则。

感谢您的帮助。

3 个答案:

答案 0 :(得分:4)

它们以多种模式存在,以管理异步数据交换和例程执行。它们也有不同的名称:

  • 承诺
  • EventEmitters
  • 延迟对象/延期
  • 控制流程库
  • 期货
  • 回调聚合器
  • Observer / Publisher-Subscriber

常见的实现是jQuery's Deferred Objects,它也用于管理它的AJAX方法。在NodeJS中,还有AsyncJS和本地EventEmitter。有些人甚至a 20-liner library实现了你可以使用的EventEmitter。

答案 1 :(得分:0)

正如Bergi在评论中所说,您正在寻找的模式称为延期/承诺。 jQuery中内置了implementation。来自文档:

  

通过调用jQuery.Deferred()创建的可链接实用程序对象   方法。它可以将多个回调注册到回调队列中,   调用回调队列,并中继任何成功或失败状态   同步或异步功能。

有许多其他实现,其中一些在此stackoverflow问题中列出。

答案 2 :(得分:0)

让自己成为队列系统,例如:

function Queue() {
    this.queue = [];
}
Queue.prototype.i = -1;
Queue.prototype.add = function(fn) {
    if (typeof fn !== "function")
        throw new TypeError("Invalid argument");

    this.queue.push(fn);
}
Queue.prototype.next = function() {
    this.i++;
    if (this.i < this.queue.length) {
        this.queue[this.i].appy(this, arguments);
    }
}
Queue.prototype.start = function() {
    if (this.i !== -1)
        throw new Error("Already running")
    this.next.apply(this, arguments);
}

并像这样使用它:

var q = new Queue();

q.add(function() {
    // do something async
    // In the callback, call `this.next()`, passing
    //    any relevant arguments
})
q.add(function() {
    // do something async
    // In the callback, call `this.next()`, passing
    //    any relevant arguments
})
q.add(function() {
    // do something async
    // In the callback, call `this.next()`, passing
    //    any relevant arguments
})

q.start();

DEMO: http://jsfiddle.net/4n3kH/