微信公众号搜"智元新知"关注
微信扫一扫可直接关注哦!

Javascript-执行长承诺时过多的递归

我有很长的异步函数序列要顺序执行.因此,我使用了Promise创建Promise队列.不幸的是,在Linux上的Firefox 25.0.1上,我收到了太多的递归JavaScript错误.它可以在Mac上的同一Firefox版本上正常运行,也可以在chrome中运行.但是我需要它在任何地方都可以工作,包括Linux.您能否提出更好的executePromiseQueueSync函数实现?

//-----------------------------------------------------------------------------------------

function executePromiseQueueSync(queue){
     var seed = $.Deferred(); 
     var acc = seed;

     for (var i = 0; i < queue.length; ++i )
     {
         var promise = queue[i];
         acc = acc.then(promise.funct.apply(null, promise.argmnt));
     }

    seed.resolve();
    return acc;
}

//-----------------------------------------------------------------------------------------

function soMetask(){
    var dfd = new jQuery.Deferred();
    dfd.notify();
    dfd.resolve();
    return dfd.promise();
}

//-----------------------------------------------------------------------------------------

$(function(){
    var promisesQueue = []

    for(var i = 0; i < 200; ++i){
        promisesQueue.push({funct:soMetask, argmnt:[]});
    }

    executePromiseQueueSync(promisesQueue).then(function(){alert('done!');});
});

//-----------------------------------------------------------------------------------------

JSF中间示例:http://jsfiddle.net/C2YN4/4/

到目前为止,我所拥有的(并且我非常乐于接受其他主张和更正):

function executePromiseQueueSync(queue){

    if (queue.length > TRESHOLD){
        var i,j;
        var shortQueue = []
        for (i=0,j=queue.length; i<j; i+=TRESHOLD) {
          temparray = queue.slice(i, i+TRESHOLD);
          shortQueue.push({funct:executePromiseQueueSync, argmnt:[temparray]});
        }
        return executePromiseQueueSync(shortQueue);
    }

    var seed = $.Deferred(); 
    var acc = seed;

    for (var i = 0; i < queue.length; ++i )
    {
         var promise = queue[i];
         acc = acc.then(promise.funct.apply(null, promise.argmnt));
    }

    seed.resolve();
    return acc;
}

因此,基本思想是制作一个承诺树而不是承诺链.这样,我们就不会深入堆栈.

例如:http://jsfiddle.net/fMBJK/1/

解决方法:

您可以使用jQuery.when〜,尽管传入200多个参数可能有些疯狂:

http://jsfiddle.net/6j6em/1/

小提琴的代码

jQuery的JavaScript

var resolves = [];

function log(msg){
  $('#log').append('<div><small>' + msg + '</small></div>');
}

function soMetask(i){
  var dfd = new $.Deferred();
  log('created task '+i);
  resolves.push(function(){
    log('resolved task '+i);
    dfd.resolve();
  });
  return dfd.promise();
}

$(function(){

  $('#resolve1').click(function(){
    for ( var i=0; i<resolves.length; i++ ) {
      resolves[i]();
    }
  });

  $('#resolve2').click(function(){
    for ( var i=0; i<resolves.length; i++ ) {
      if ( i == 5 ) continue;
      resolves[i]();
    }
  });

  $('#resolve3').click(function(){
    resolves[5]();
  });

  var i, queue = []; for(i=0; i<200; ++i){ queue.push(soMetask(i)); }

  (jQuery.when.apply(jQuery, queue))
    .done(function(){
      log('all resolved!');
      alert('all resolved!');
    })
  ;

});

此示例的标记

<button id="resolve1">Resolve all</button>
&nbsp;&nbsp;
<button id="resolve2">Resolve (all but one)</button>
<button id="resolve3">Resolve (the remaining one)</button>
<div id="log"></div>

此示例的CSS

#log {
    border: 1px solid black; 
    padding: 10px; 
    width: 400px; 
    height: 200px; 
    overflow: auto;
}

说明

上面的许多代码只是为了说明,关键点是:

  var i, queue = []; for(i=0; i<200; ++i){ queue.push(soMetask(i)); }

  (jQuery.when.apply(jQuery, queue))
    .done(function(){
      log('all resolved!');
      alert('all resolved!');
    })
  ;

上面的代码生成一个Promise对象数组,然后使用apply将它们传递给jQuery.when,然后当它们全部完成后,它会处理创建正确的结构以触发完成的回调.那就是如果您想要这种行为.如果您想要一个系统来等待每个promise对象按顺序解析,那么在触发下一个任务之前,您将需要一些不同的东西.

更新资料

在顺序执行任务方面,您需要使用其他方法,例如:

http://jsfiddle.net/6j6em/2/

function log(msg){
  $('#log').append('<div><small>' + msg + '</small></div>');
}

function soMetask(i){
  var dfd = new $.Deferred();
  log(':: created task '+i);
  setTimeout(function(){
    log(':: resolved task '+i);
    dfd.resolve();
  },50);
  return dfd.promise();
}

$(function(){

  var Queue;

  Queue = function( items ){ 
    this.items = items.slice(0);
    this.promise = $.Deferred();
  };
  Queue.prototype.next = function(){
    log(':: next task triggered');
    var q = this;
    q.lastItem = q.items.shift();
    if ( q.lastItem ) {
      q.lastPromise = q.lastItem.func.apply( null, q.lastItem.args );
      q.lastPromise.then(function(){
        /// include a setTimeout 0 to avoid possible stack/recursion errors.
        setTimeout(function(){
          q.next();
        },0);
      });
    }
    else {
      /// we are finished
      q.promise.resolve();
    }
  };
  Queue.prototype.run = function(){
    this.next();
  };

  var i, items = []; for(i=0; i<200; ++i){ 
    items.push({ func: soMetask, args:[i] });
  }

  var q = new Queue( items );
  q.promise.done(function(){
    log(':: done!');
    alert('Done!');
  });
  q.run();

});

这将构建一个定制的Queue对象,该对象跟踪承诺列表,并在第一个成功后触发下一个.但是,此代码显然需要错误处理.

更新x2

每个承诺都不能依赖进度,因为在任何时候都只有一个被触发.您可以将自己的通知调用添加到整个$.Deferred()对象.

var Queue;
Queue = function( items ){ 
  this.items = items.slice(0);
  this.promise = $.Deferred();
  this.count = 0;
};
Queue.prototype.next = function(){
  log(':: next task triggered');
  var q = this;
  q.lastItem = q.items.shift();
  if ( q.lastItem ) {
    q.lastPromise = q.lastItem.func.apply( null, q.lastItem.args );
    q.lastPromise.then(function(){
      q.promise.notify(q.count++);
      q.next();
    });
  }
  else {
    q.promise.resolve();
  }
};
Queue.prototype.run = function(){
  this.next();
};
var q = new Queue( items );
q.promise
  .done(function(){log(':: done!');})
  .progress(function(p){log('::progress ' + p);})
;
q.run();

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 [email protected] 举报,一经查实,本站将立刻删除。

相关推荐