执行promises数组数组并在每个序列之后依次运行验证回调

时间:2017-05-16 13:49:41

标签: javascript jquery promise

我努力制作一系列承诺数组。这背后的想法如下:

计划是以下对象的数组:

psql

承诺是AJAX调用(在我的例子中,我用随机超时后解析的假承诺模拟它们)。

然后按优先级对计划进行排序,并执行其操作。在下一系列操作的验证回调之后,应在所有回调之后 运行当前系列返回[ { priority: 0, actions: [Promise, Promise, Promise, ...] }, { priority: 1, actions: [Promise, Promise, Promise, ...] } ... ] 。如果任何Promise被拒绝或验证结果失败,则执行应该停止。换句话说,我需要它在以下场景中工作:

  
      
  1. progressBar.activate() - 蓝色。
  2.   
  3. 所有系列的所有承诺都是异步启动的。
  4.   
  5. 当优先级为0的结果到达时,请异步验证它们。不要验证以下系列的结果。      
        
    • 任何验证失败?任何优先级为0的承诺都会被拒绝?      
          
      • YES:progressBar.error() - 红色;马上回来。
      •   
    •   
    • 优先级为0的所有验证Promise都可以吗?      
          
      • 是:转到下一步。
      •   
    •   
  6.   
  7. 验证优先级为1的结果。      
        
    • ...
    •   
    • ...
    •   
  8.   
  9. 验证优先级为2的承诺的结果。      
        
    • ...
    •   
    • ...
    •   
  10.   
  11. 全部完成,progressBar.deactivate() - 橙色。
  12.   

但是我不能按顺序运行 - 所有系列的结果都是在到达时以随机顺序验证的!我想我无法在我的true方法中正确链接调用。并且在进程完成之前,progressBar也会异步停用:

syncRun()

任何建议表示赞赏。这是完整的代码:



var syncRun = function (schedule) {
    var tasks = sortSchedule(schedule);
    progressBar.activate();
    var chain = $.Deferred().resolve();
    tasks.forEach(function (step) {
        // increase the total empty width of progressBar
        progressBar.totalProgress += step.length;
        // chain the next series of promises
        chain = chain.then(function () {
                return Promise.all(step);
            }
        );
    });
    // chain the final action: deactivate progressBar
    chain = chain.then(function () {
        progressBar.deactivate();
    }, function () {});
};

var progressBar = {
  activate: function() {
    $('#progress-bar').addClass('active');
  },
  deactivate: function() {
    $('#progress-bar').removeClass('active');
  },
  error: function() {
    $('#progress-bar').addClass('error');
  },
  current: 0,
  total: 0,
  setWidth: function() {
    var widthPercent = 0;
    if (this.total !== 0) {
      widthPercent = this.current / this.total * 100 + '%';
    }
    $('#progress-bar').css('width', widthPercent);
  },
  get totalProgress() {
    return this.total;
  },
  set totalProgress(value) {
    this.total = value;
    this.setWidth();
  },
  get currentProgress() {
    return this.current;
  },
  set currentProgress(value) {
    this.current = value;
    this.setWidth();
  }
};

var logger = function(message) {
  $('<p></p>').text(message).appendTo($('#logger'));
};

var resolveLimit = 6;
var validatorLimit = 6;

var fakeAjax = function(id) {
  return new Promise(function(resolve, reject) {
    setTimeout(function() {
      if (id <= resolveLimit) {
        resolve(id);
      } else {
        reject(id);
      }
    }, (Math.random() * 5 | 0) * 1000);
  });
};

var action = function(request, callback) {
  request.then(function(result) {
    progressBar.currentProgress++;
    var isValid = callback(result);
    if (!isValid) {
      throw 'Rejected ' + result;
    }
  });
};

var validator = function(result) {
  if (result <= validatorLimit) {
    logger('Series #' + result + ' parsed');
    return true;
  } else {
    logger('Series #' + result + ' rejected');
    progressBar.error();
    return false;
  }
};

// Generate an array of action objects of specified length
var guid = 0;
var generateActions = function(count) {
  var result = [];
  for (var i = 0; i < count; i++) {
    result.push(
      action(fakeAjax(guid), validator)
    );
  }
  guid++;
  return result;
};


var sortSchedule = function(schedule) {
  var tasks = [];
  schedule.forEach(function(step) {
    if (!tasks[step.priority]) {
      tasks[step.priority] = [];
    }
    tasks[step.priority] = tasks[step.priority].concat(step.actions);
  });
  return tasks.sort(function(a, b) {
    return tasks[b] - tasks[a];
  });
};

var syncRun = function(schedule) {
  var tasks = sortSchedule(schedule);
  progressBar.activate();
  var chain = $.Deferred().resolve();
  tasks.forEach(function(step) {
    // increase the total empty width of progressBar
    progressBar.totalProgress += step.length;
    // chain the next series of promises
    chain = chain.then(function() {
      return Promise.all(step);
    });
  });
  // chain the final action: deactivate progressBar
  chain = chain.then(function() {
    progressBar.deactivate();
  }, function() {});
};

var schedule = [{
    priority: 0,
    actions: generateActions(6)
  },
  {
    priority: 1,
    actions: generateActions(5)
  },
  {
    priority: 2,
    actions: generateActions(4)
  },
  {
    priority: 3,
    actions: generateActions(3)
  },
  {
    priority: 4,
    actions: generateActions(2)
  },
  {
    priority: 5,
    actions: generateActions(1)
  }
];

syncRun(schedule);
&#13;
.progress-bar-container {
  border: #999999 1px solid;
  width: 90%;
  padding: 5px;
  margin-bottom: 10px;
}

@keyframes blinker {
  50% {
    opacity: 0.5;
  }
}

.progress-bar {
  background-color: #FF9933;
  height: 4px;
  animation: blinker 1s linear infinite;
  width: 100%;
}

.active {
  background-color: #0099FF;
}

.error {
  background-color: #FF0000 !important;
}
&#13;
&#13;
&#13;

2 个答案:

答案 0 :(得分:0)

好的,所以我不完全确定这是否是一个很好的解决方案,但是如果可以从中派生出任何值,我将一个在构造函数中承诺的类汇集在一起​​,当强制a时在最终返回累积结果之前等待每个分辨率的承诺序列:

class ChainRequest {
  constructor(promise) {
    this.promise = promise;
    this.nextPromise = null;
  }

  appendNextPromise (nextPromise) {
    this.nextPromise = nextPromise;
  }

  execute (prevResults) {
    this.results = prevResults;
    return this.promise()
    .then(result => {
      this.results.push(result);
      if (this.nextPromise !== null) {
        return this.nextPromise.execute(this.results);
      }
      else {
        return Promise.resolve(this.results);
      }
    });
  }
}

let chainPromises = [promise1, promise2, promise3...];

let currentPromise = new ChainRequest(promise1);
let headPromise = currentPromise;

for (let i = 1; i < chainPromises.length; i++) {
  let nextPromise = new ChainRequest(chainPromises[i));
  currentPromise.appendNextPromise(nextPromise);
  currentPromise = nextPromise;
}

return headPromise
.then(results => {
  console.log(results);
});

答案 1 :(得分:0)

首先,更改您的计划,以便列出操作而不是立即调用它们:

var schedule = [{
    priority: 0,
    actions: 6 // instead of generateActions(6)
  },
  {
    priority: 1,
    actions: 5
  },
  {
    priority: 2,
    actions: 4
  },
  {
    priority: 3,
    actions: 3
  },
  {
    priority: 4,
    actions: 2
  },
  {
    priority: 5,
    actions: 1
  }
];

然后在你真正想要这样做的地方调用它们:

var chain = Promise.resolve(); // <- No need to use $.Deferred here
tasks.forEach(function(stepLength) {
  // increase the total empty width of progressBar
  progressBar.totalProgress += stepLength;
  // chain the next series of promises
  chain = chain.then(function() {
    return Promise.all(generateActions(stepLength));
  });
});