返回在所有待处理的承诺完成后解决的承诺

时间:2018-04-09 22:56:52

标签: javascript promise

我有一个函数save,可以多次调用并对我们的服务器进行AJAX调用。虽然该功能仍在保存,但我想在页面上显示一个指示符,该指示符将在所有保存请求完成后消失。我希望从save返回的承诺在ALL请求完成后解析。我不能简单地使用Promise.all,因为在所有保存请求完成之前可能会有更多请求进入。例如:

let pending_promises = [];

function addPromise(index, time = 5000) {
  console.log('adding promise ' + index);
  var promise = new Promise((resolve) => {
    window.setTimeout(() => {
      resolve();
    }, time);
  });

  pending_promises.push(promise);

  return promise
    .then(() => {
      console.log('done with promise ' + index);
      return Promise.all(pending_promises);
    })
    .then(() => {
      pending_promises = [];
    });
}

addPromise(1, 5000).then(() => {
  console.log('DONE WITH THEM ALL');
})

addPromise(2, 1000);
addPromise(3, 6000);

window.setTimeout(() => {
  addPromise(4, 2000);
}, 5500);

这已经接近工作了,但是console.log说" DONE WITH THEM ALL"将在第4个承诺解决之前打印。任何人都可以提供一些帮助吗?

2 个答案:

答案 0 :(得分:2)

你真的不需要保留一系列的承诺。相反,你可以跟踪尾巴的承诺。每当一个承诺结算,检查它是否仍然是尾巴。如果它仍然是尾巴,那么链条中没有更多的承诺,所以一切都完成了。这是编写代码的一种方法:

let promiseTail = Promise.resolve();
let opInFlight = false;

// simulated ajax call
function delay(t, v) {
    return new Promise(function(resolve) {
        setTimeout(resolve.bind(null, v), t);
    });
}

function someAsyncCall(index, t) {
    if (!opInFlight) {
        console.log("----------------------------");
        console.log("Starting chain of operations");
        opInFlight = true;
        // show spinner here
    }

    // insert your actual ajax call here
    console.log('starting async operation ' + index);
    let p = delay(t).then(function(result) {
        console.log('done with async operation ' + index);
        return result;
    });

    let thisPromise;

    function complete() {
        // if our promise was also the promiseTail, 
        //   then everything that was in flight is now done
        if (promiseTail === thisPromise) {
            console.log("All done with chain of operations");
            console.log("---------------------------------");
            opInFlight = false;
            // hide spinner here
        }
        return p;
    }

    // now hook this promise onto the chain
    // and remember it's value locally so it can be compared later
    thisPromise = promiseTail = promiseTail.then(complete, complete);

    // return this specific promise so a caller can know when this specific
    // async operation is done
    return p;
}

// testing...

someAsyncCall(1, 5000);
someAsyncCall(2, 1000);
someAsyncCall(3, 6000);
someAsyncCall(4, 2000).then(function(result4) {
    console.log("async operation 4 .then() handler called");
});

// now start one at little later to make sure it makes it into the chain too
setTimeout(function() {
    someAsyncCall(5, 1000);
}, 2500);

// now start some after the above have all finished to see if another chain works
// and we can detect the second completion

setTimeout(function() {
    someAsyncCall(10, 2000);
    someAsyncCall(11, 1000);
}, 7000);

代码说明:

  1. 跟踪两个州。我们添加到链中的最后一个承诺和一个告诉我们以前没有任何东西在飞行中的布尔值。
  2. 每当我们开始一项新操作时,我们都会将它添加到承诺链的末尾,并将链的尾部设置为新的承诺。
  3. 我们在链上的每个承诺上添加一个.then()处理程序,每当我们看到.then()火时,链的末端仍然是我们将我们添加到链中后的结果,然后链中一定没有其他东西,所以链现在是空的。我们通知链是空的,然后重置opInFlight标志(这允许我们稍后检测我们何时开始新链)。
  4. 此设计假设被拒绝的承诺应该只是保持链处理正常(不中止链),以便ajax调用应该只计算为已完成,即使它失败(为了这个会计目的)。
  5. 注意:一个漂亮的干净设计会创建一个eventEmitter对象并发出几个事件:

    1. “ajaxFlagBegin” - 当它从飞行中没有ajax操作转移到飞行中的ajax操作时。
    2. “ajaxFlagEnd” - 当它回到飞行中没有ajax操作时
    3. “ajaxStart” - 当任何ajax操作开始时
    4. “ajaxEnd” - 当任何ajax操作完成时
    5. 然后,任何来电者都可以倾听他们最感兴趣的事件。

答案 1 :(得分:0)

如果之前通话的结果并不重要,那么您无需担心它们。

我对这种情况的伪代码(我经常遇到它)通常是这样的:

var myRequest;

function save() {
   //it's important that you abort the previous ajax call
   //before you show the spinner
   if(myRequest) { 
        myRequest.abort(); 
    }

   showSpinner();
   myRequest = //your ajax call

   //hide the spinner regardless of the result of the request
   myRequest.then(
     function() { hideSpinner(); },
     function() { hideSpinner(); }
   );

   //return the request
   return myRequest;
};

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