编程语言
首页 > 编程语言> > Javascript-执行长承诺时过多的递归

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

标签:promise,jquery-deferred,javascript,algorithm,recursion
来源: https://codeday.me/bug/20191122/2059726.html