如何实现可取消的,有序的承诺?

时间:2016-12-31 05:45:51

标签: javascript promise es6-promise

我已经举了一个例子来展示我的目标:



function onInput(ev) {
  let term = ev.target.value;
  console.log(`searching for "${term}"`);
  getSearchResults(term).then(results => {
    console.log(`results for "${term}"`,results);
  });
}

function getSearchResults(term) {
  return new Promise((resolve,reject) => {
    let timeout = getRandomIntInclusive(100,2000);
    setTimeout(() => {
       resolve([term.toLowerCase(), term.toUpperCase()]);  
    }, timeout);
    
  });
}

function getRandomIntInclusive(min, max) {
  min = Math.ceil(min);
  max = Math.floor(max);
  return Math.floor(Math.random() * (max - min + 1)) + min;
}

<input onInput="onInput(event)">
&#13;
&#13;
&#13;

输入&#34;搜索&#34;盒子,看着控制台。搜索结果不按顺序返回!

如果有新的输入并保证结果按顺序返回,我们如何取消任何待处理的承诺?

5 个答案:

答案 0 :(得分:3)

我没有使用去抖或超时,而是使用引用函数在此函数内部设置少量状态之外(由Jaromanda X建议)。这样,您只需将函数引用更改为noop之类的内容即可。承诺仍然有效,但不会采取任何行动。但是,最后一个不会更改其功能参考:

var onInput = function() {
  let logger = function(term, results) {
    console.log(`results for "${term}"`, results);
  };
  let noop = Function.prototype;
  let lastInstance = null;

  function ActionManager(action) {
    this.action = action;
  }

  return function onInput(ev) {
    let term = ev.target.value;
    console.log(`searching for "${term}"`);

    if (lastInstance) {
      lastInstance.action = noop;
    }

    let inst = new ActionManager(logger.bind(null, term));
    lastInstance = inst;

    getSearchResults(term).then(response => inst.action(response));
  }
}();



/****************************************
 * The rest of the JavaScript is included only for simulation purposes
 ****************************************/

function getSearchResults(term) {
  return new Promise((resolve, reject) => {
    let timeout = getRandomIntInclusive(100, 2000);
    setTimeout(() => {
      resolve([term.toLowerCase(), term.toUpperCase()]);
    }, timeout);

  });
}

function getRandomIntInclusive(min, max) {
  min = Math.ceil(min);
  max = Math.floor(max);
  return Math.floor(Math.random() * (max - min + 1)) + min;
}
<input onInput="onInput(event)">

答案 1 :(得分:3)

您可以使用Promise.race取消上一个链的效果:

let cancel = () => {};

function onInput(ev) {
  let term = ev.target.value;
  console.log(`searching for "${term}"`);
  cancel();
  let p = new Promise(resolve => cancel = resolve);
  Promise.race([p, getSearchResults(term)]).then(results => {
    if (results) {
      console.log(`results for "${term}"`,results);
    }
  });
}

function getSearchResults(term) {
  return new Promise(resolve => {
    let timeout = 100 + Math.floor(Math.random() * 1900);
    setTimeout(() => resolve([term.toLowerCase(), term.toUpperCase()]), timeout);
  });
}
<input onInput="onInput(event)">

我们这样做是通过注入undefined结果并对其进行测试来实现的。

答案 2 :(得分:1)

一个可行的解决方案是包含firstf并简单地忽略任何早期时间戳(因而过时)的响应。

latestTimestamp
let latestTimestamp = 0;

function onInput(ev) {
  let term = ev.target.value;
  console.log(`searching for "${term}"`);
  latestTimestamp = Date.now();
  getSearchResults(term, latestTimestamp).then(results => {
    if (results[2] !== latestTimestamp) {
      console.log("Ignoring old answer");
    } else {
      console.log(`results for "${term}"`, results);
    }
  });
}

function getSearchResults(term, latestTimestamp) {
  return new Promise((resolve, reject) => {
    let timeout = getRandomIntInclusive(100, 2000);
    setTimeout(() => {
      resolve([term.toLowerCase(), term.toUpperCase(), latestTimestamp]);
    }, timeout);

  });
}

function getRandomIntInclusive(min, max) {
  min = Math.ceil(min);
  max = Math.floor(max);
  return Math.floor(Math.random() * (max - min + 1)) + min;
}

答案 3 :(得分:-1)

您可以使用async包 - 一堆实用程序来维护异步代码。它最初是为node.js开发的,但它也可以用在前端 您需要series功能,它会保存承诺的顺序。以下是coffeescript中的一个简短示例:

async.series([
  ->
    ### do some stuff ###
    Q 'one'
  ->
    ### do some more stuff ... ###
    Q 'two'
]).then (results) ->
    ### results is now equal to ['one', 'two'] ###
    doStuff()
  .done()

### an example using an object instead of an array ###
async.series({
  one: -> Q.delay(200).thenResolve(1)
  two: -> Q.delay(100).thenResolve(2)
}).then (results) ->
    ### results is now equal to: {one: 1, two: 2} ###
    doStuff()
  .done()  

见caolan.github.io/async /

答案 4 :(得分:-2)

你不应该像你所做的那样在承诺中使用setTimeout,因为从.then你要回复来自.setTimeout()的回调,这将无法正常工作和混乱订单。为了使承诺顺利进行,您应该创建如下所示的函数:

function wait(n){
    return new Promise(function(resolve){
        setTimeout(resolve, n)
    });
}

并使用该函数替换setTimeout(),如下所示:

wait(getRandomIntInclusive(100,2000)).then(function(){
    // code
});