Promise - можно ли принудительно отменить обещание
Я использую обещания ES6 для управления всеми моими сетевыми данными, и есть некоторые ситуации, когда мне нужно заставить их отменить.
в основном сценарий таков, что у меня есть опережающий поиск по типу в пользовательском интерфейсе, где запрос делегируется бэкэнду, должен выполнять поиск на основе частичного ввода. В то время как этот сетевой запрос (#1) может занять немного времени, пользователь продолжает вводить, что в конечном итоге вызывает другой бэкэнд-вызов (#2)
здесь #2 естественно, имеет приоритет над #1, поэтому я хотел бы отменить запрос на обертывание обещания #1. У меня уже есть кэш всех обещаний на уровне данных, поэтому я могу теоретически получить его, когда я пытаюсь представить обещание для #2.
но как я могу отменить обещание #1, Как только я получу его из кэша?
может ли кто-нибудь предложить подход?
3 ответа:
нет. Мы пока не можем этого сделать.
обещания ES6 не поддерживают отмену и все же. Он уже в пути, и его дизайн-это то, над чем многие люди работали очень тяжело. звук семантика отмены трудно получить право, и это работа в процессе. Есть интересные дебаты по РЕПО" fetch", по esdiscuss и по нескольким другим РЕПО на GH, но я бы просто был терпелив на вашем месте.
но, Но, но.. отмена действительно важно!
это так, реальность дела в том, что отмена есть действительно важный сценарий в программировании на стороне клиента. Случаи, которые вы описываете, как прерывание веб-запросов, важны, и они повсюду.
так... язык меня подвел!
да, извините за это. Обещания должны были попасть в первую очередь, прежде чем дальнейшие вещи были указаны - поэтому они вошли без некоторых полезных вещей, таких как
.finally
и.cancel
- он уже в пути хотя, к спецификации через DOM. Отмена - это не запоздалая мысль это просто ограничение по времени и более итеративный подход к дизайну API.так что я могу сделать?
у вас есть несколько вариантов:
- используйте стороннюю библиотеку, например птица кто может двигаться намного быстрее, чем спецификации и, таким образом, имеют отмену, а также кучу других лакомств - это то, что крупные компании, такие как WhatsApp делать.
- пройти отмене маркер.
использование сторонней библиотеки довольно очевидно. Что касается токена, вы можете заставить свой метод взять функцию, а затем вызвать ее, как таковую:
function getWithCancel(url, token) { // the token is for cancellation var xhr = new XMLHttpRequest; xhr.open("GET", url); return new Promise(function(resolve, reject) { xhr.onload = function() { resolve(xhr.responseText); }); token.cancel = function() { // SPECIFY CANCELLATION xhr.abort(); // abort request reject(new Error("Cancelled")); // reject the promise }; xhr.onerror = reject; }); };
что позволит вам сделать:
var token = {}; var promise = getWithCancel("/someUrl", token); // later we want to abort the promise: token.cancel();
ваш фактический случай использования -
last
это не слишком сложно с приближением знак:
function last(fn) { var lastToken = { cancel: function(){} }; // start with no op return function() { lastToken.cancel(); var args = Array.prototype.slice.call(arguments); args.push(lastToken); return fn.apply(this, args); }; }
что позволит вам сделать:
var synced = last(getWithCancel); synced("/url1?q=a"); // this will get canceled synced("/url1?q=ab"); // this will get canceled too synced("/url1?q=abc"); // this will get canceled too synced("/url1?q=abcd").then(function() { // only this will run });
и нет, библиотеки, такие как Bacon и Rx, не "сияют" здесь, потому что они наблюдаемые библиотеки, они просто имеют то же преимущество, что и библиотеки обещаний пользовательского уровня, не будучи связанными спецификациями. Я думаю, мы будем ждать, чтобы иметь и видеть в ES2016, когда наблюдаемые становятся родными. Они are отличный для typeahead, хотя.
стандартные предложения по отмене обещания так и не удалось.
обещание не является управляющей поверхностью для асинхронного действия, выполняющего его; путает владельца с потребителем. Вместо этого создайте асинхронный функции это может быть отменено через какой-то переданный токен.
еще одно обещание делает прекрасный токен, что делает отмену легко реализовать с
Promise.race
:пример: использовать
Promise.race
чтобы отменить эффект предыдущая цепочка:let cancel = () => {}; input.oninput = function(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); }); }
Search: <input id="input">
здесь мы "отменяем" предыдущие поиски, вводя
undefined
результат и тестирование для него, но мы могли бы легко представить себе отказ с"CancelledError"
вместо.конечно, это на самом деле не отменяет сетевой поиск, но это ограничение
fetch
. Еслиfetch
должны были принять отмену обещание в качестве аргумента, то он может отменить сетевую активность.Я предложил это "отменить обещание шаблон" на es-обсудить, именно чтобы предложить, что
fetch
сделать это.
Я проверил ссылку Mozilla JS и нашел это:
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise/race
давайте проверим это:
var p1 = new Promise(function(resolve, reject) { setTimeout(resolve, 500, "one"); }); var p2 = new Promise(function(resolve, reject) { setTimeout(resolve, 100, "two"); }); Promise.race([p1, p2]).then(function(value) { console.log(value); // "two" // Both resolve, but p2 is faster });
у нас здесь p1, а p2 положить в
Promise.race(...)
в качестве аргументов это фактически создает новое обещание разрешения, которое вам требуется.