Повторяющиеся записи в бесконечном загрузчике и фильтре

#javascript #vue.js #axios

Вопрос:

Я использую vue-бесконечный загрузчик для отображения пользователей и загрузки их на прокрутку. Кроме того, у меня есть фильтр по имени.

Я следовал инструкциям, данным на https://peachscript.github.io/vue-infinite-loading/guide/use-with-filter-or-tabs.html

Вот HTML — код для фильтра:

  <input v-model="name" type="text" class="form-control" placeholder="Search by name" @input="changeName">
 

И здесь соответствующие методы vue:

  methods: {
        infiniteHandler: function ($state) {
            console.log(api   this.parameter);
            axios.get(api   this.parameter, {params: {page: this.page}}).then((result) =>   {
                if (result.data.meta.current_page <= result.data.meta.last_page) {
                    this.page  = 1;
                    this.users.push(...result.data.data);

                    $state.loaded();
                } else {
                    $state.complete();
                }
            });
        },
        changeName() {
             this.parameter= '?name='   encodeURIComponent(this.name);
             this.page = 1;
             this.users = [];
             this.infiniteId  = 1;                          
        },
 

Если я сейчас быстро введу «Макс» в качестве имени пользователя во введенном текстовом файле, то changeName это будет выполнено 3 раза, один раз с M , Ma и Max . Проблема в том, что вызовы выполняются параллельно, то есть пользователи, начинающиеся с Max , также будут совпадать для начала с Ma и M . Таким образом, у меня внезапно появились дубликаты, а также пользователь с именами, которые начинаются не с Max , а с Marjon и т. Д.

На самом деле у меня changeName() есть тайм-аут, чтобы предотвратить HTTP-вызов при каждом нажатии клавиши:

  changeName() {            
     clearTimeout(this.timeout);
     this.timeout = setTimeout(function() { ...}, 200);
 },
 

Однако теоретическая проблема, описанная выше, все еще возможна здесь (если нажатия клавиш и вызовы HTTPS занимают более 200 мс).

Я хотел бы, чтобы затем, если пользователь вводит «Ma», а затем «Max», вызов axios, инициированный «Ma», был остановлен и немедленно был выполнен вызов «Max».

Ответ №1:

Чтобы отменить запросы axios, вы можете использовать canceltoken

Обратите внимание, как любой предыдущий запрос отменяется при вызове infiniteHandler — и что маркер отмены удаляется по завершении запроса

Таким образом, никакого дополнительного кода, кроме нескольких строк здесь, не требуется

 methods: {
    infiniteHandler: function ($state) {
        console.log(api   this.parameter);
        // _cancelSource is truthy if there's a request "in flight"
        if (this._cancelSource) {
            this._cancelSource.cancel('new search');
            this._cancelSource = null;
        }
        // get a new canceToken
        this._cancelSource = axios.CancelToken.source();
        axios.get(api   this.parameter, {
            cancelToken: this._cancelSource.token
            params: {page: this.page}
        }).then((result) =>   {
            // no need to cancel now
            this._cancelSource = null;
            if (result.data.meta.current_page <= result.data.meta.last_page) {
                this.page  = 1;
                this.users.push(...result.data.data);
                $state.loaded();
            } else {
                $state.complete();
            }
        });
    },
 

В случае, если отмена не совсем сработает — вы можете связать обещания, возвращенные axios

Однако я не верю, что это потребуется

Все еще может быть полезно, если вы не хотите отменять предыдущий запрос (для какой-либо другой цели), удалив код cancelToken

 methods: {
    infiniteHandler: function ($state) {
        console.log(api   this.parameter);
        
        // initialise the promise for first time
        this._requestPromise = this._requestPromise || Promise.resolve();
        
        // _cancelSource is truthy if there's a request "in flight"
        if (this._cancelSource) {
            this._cancelSource.cancel('new search');
            this._cancelSource = null;
        }
        // get a new canceToken
        this._cancelSource = axios.CancelToken.source();
        
        // chain on to the previous request
        // NOTE: cancelling the previous request will make it reject ... 
        //      but that can be handled
        this._requestPromise = this._requestPromise
         // handle any cancelled previous request
         // or previous request rejections
         // we still want to do this one if the previous rejected
        .catch(() => {}) 
        .then(() => {
            axios.get(api   this.parameter, {
                cancelToken: this._cancelSource.token
                params: {page: this.page}
            }).then((result) =>   {
                // no need to cancel now
                this._cancelSource = null;
                if (result.data.meta.current_page <= result.data.meta.last_page) {
                    this.page  = 1;
                    this.users.push(...result.data.data);
                    $state.loaded();
                } else {
                    $state.complete();
                }
            });
        });
    },
 

Ответ №2:

Вы можете отменить запрос axios прямо перед тем, как другой начнет использовать функцию отмены axios

 const CancelToken = axios.CancelToken;
const source = CancelToken.source();

axios.get(api   this.parameter, {
    params: {page: this.page}},
    cancelToken: source.token
).then((result) =>   {
    if (result.data.meta.current_page <= result.data.meta.last_page) {
        this.page  = 1;
        this.users.push(...result.data.data);
        $state.loaded();
    } else {
        $state.complete();
    }
});
 

(Код не проверен, но дает представление)