2016-12-31 44 views
3

我已经把一个例子来说明什么,我在得到:如何实现可取消,有序的承诺?

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)">

键入“搜索”框,看控制台。搜索结果不合时宜!

当有新的输入并保证结果回来时,我们如何取消任何未决的承诺?

+0

您可能对[debounce function](https://davidwalsh.name/javascript-debounce-function)感兴趣。不通过 – Phil

+0

回答订购问题订单问题是由于您的超时随机化。以后的请求可能会解决之前的请求 – Phil

+0

@Phil是的,我明白*为什么*这是发生,我问问什么是一个好的方法来处理它?随机超时是​​模拟网络请求可能需要多长时间。 – mpen

回答

1

一个可行的解决方案是包括一个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; 
 
}
<input onInput="onInput(event)">

+0

我认为一个简单的计数器会比时间戳更好的工作(如果两个请求在同一个ms中出现,则不会出现这种情况),但这个想法是可靠的。 – mpen

+0

@mpen是的,你是对的 - 在这种情况下,一个简单的柜台会更好。 (上次我做了类似的事情,我们需要时间戳,所以我的脑袋有点卡在这里。) –

-2

你不应该在承诺使用setTimeout的你正在做的方式,因为从.then您返回从.setTimeout()回调这是行不通的扰乱了订购。为了使承诺去为了你应该做一个函数如下所示:

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

,并与该功能替代setTimeout()就像如下图所示:

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

请参阅问题下的注释,超时用于模拟网络响应时间变化。 –

-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/

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)">

+0

我喜欢这个答案,但是作为记录器,noop,lastInstance和AcrionManager都只在'onInput'内部相关 - 你可以使'state'成为像[这个小提琴]一样(https://jsfiddle.net/3sfqdp8y/) –

+0

@JaromandaX,好点。我已更新您的建议。谢谢! – KevBot

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结果和测试。

+0

这是一个非常有趣的方法。还没有看到有人拿出这样的“解决方法”。我有点喜欢它! – mpen