Как создать специальный API вытягивания из XMLHttpRequest

kak sozdat speczialnyj api vytyagivaniya iz

Какой твой худший кошмар?

Это прозвучало темно, но это не риторический вопрос. Я очень хочу знать, потому что я собираюсь рассказать вам свое. Попутно мы узнаем некоторые вещи, например как работает API выборки, а также как работают конструкторы функций.

Извините, что отвлекаюсь, возвращаясь к своему худшему кошмару. Если бы вы задали мне этот вопрос на прошлой неделе, это был бы список ниже без особого порядка:

  • Написание синтаксиса Pre-ES6
  • Нет API для получения
  • Без транспилятора (Babel/машинопись)
  • Дядя Боб сказал, что я разочарован (шучу)

Если ваш список совпадает с моим, то я должен сказать, что вы очень удивительный человек. К счастью, меня призвали работать над проектом, оживившим мой список кошмаров (за исключением последнего). Я должен был добавить новую функцию в программу. Это была устаревшая кодовая база, использовавшая чисто синтаксис pre-es6 и XMLHttpRequest (ужас) для своих запросов AJAX.

Поэтому, пытаясь сделать опыт приятным, я решил создать функцию, которая отвлекает все запросы AJAX, которые я делаю, и открывать API, имитирующий новый API получения (на самом деле не так). Это также после того, как я просмотрел Javascript: новое видео о сложных частях о мастерах интерфейса, где было дано удивительное объяснение того, как работает API выборки под капотом. Давайте начнем.

Сначала мне нужно было посмотреть, как работает XMLHttpRequest. Потом начал писать функцию. Моя первая итерация выглядела так:

"use strict";


function fetch() {
  var url = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : '';
  var options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};

var xhr = new XMLHttpRequest();
  var onFufillment = [];
  var onError = [];
  var onCompletion = [];
  var method = "GET" || options.method;
  xhr.onreadystatechange = function () {
    var _data = this;
    if (this.readyState == 4 && this.status == 200) {
      // Action to be performed when the document is read;
      onFufillment.forEach(function (callback) {
          callback(_data);
      });
     onCompletion.forEach(function (callback) {
        callback(_data);
      });
    } else if (this.readyState == 4 && this.status !== 200) {
      onError.forEach(function (callback) {
        callback(_data);
      });
      onCompletion.forEach(function (callback) {
        callback(_data);
      });
    }
  };
  xhr.open(method, url, true);
  xhr.send();


return {
    then: function then(fufillmentFunction) {
      onFufillment.push(fufillmentFunction);
    },
    catch: function _catch(errorFunction) {
      onError.push(errorFunction);
    },
    finally: function _finally(completionFunction) {
      onCompletion.push(completionFunction);
    }
  };
}

Позвольте мне рассмотреть, что делает функция:

  • Мы проверяем, есть ли url аргумент передается в функцию. По умолчанию пустая строка, если ничего не передано
  • Мы также делаем то же самое для options аргумент. По умолчанию пуст объект, если ничего не передано
  • Затем мы создаем новый экземпляр XMLHttpRequest
  • Создаем 4 переменные onFufillment, onError, onCompletion and method
  • onFufillment является массивом, который сохраняет все функции, передаваемые в then метод
  • onError является массивом, который сохраняет все функции, передаваемые в catch метод
  • onCompletion является массивом, который сохраняет все функции, передаваемые в finally метод
  • method используется для хранения используемого метода HTTP по умолчанию GET
  • Затем мы передаем функцию в onreadystatechange методом xhr который будет вызван, когда состояние запроса изменится
  • В функции мы сохраняем this в а _data переменной, чтобы ее можно было передать в функции forEach без потери контекста (я знаю this раздражает)
  • Затем мы проверяем, выполнен ли запрос (readyState == 4 ) и если запрос успешен, мы перебираем onFufillment and onCompletion массивы, вызывая каждую функцию и передавая _data в это
  • Если запрос не выполняется, мы делаем то же самое с onCompletion and onError массивы
  • Затем мы отправляем запрос с переданными параметрами
  • После этого мы возвращаем объект, содержащий три функции. catch and finally которые имеют те же названия, что и API получения.
  • catch толкает функцию, которая передается как аргумент, в onError массив
  • then делает то же самое с onFufillment массив
  • finally делает то же самое с onCompletion массив

Использование этого API будет выглядеть так:

var futureData = fetch('
futureData.then(function(data){
  console.log(data)
})

futureData.finally(function(response){
  console.log(response);
});

futureData.catch(function(error){
  console.log(error);
})

Это работает!!! Но не так, как реальная реализация выборки. Можем ли мы сделать лучше этого? Конечно, можем. Мы все еще можем добавить больше функций в функцию. Мы могли бы сделать его цепным, то есть мы можем предоставить ему возможность объединять методы вместе.

На второй итерации это выглядит так:

"use strict";

function fetch() {
  var url = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : '';
  var options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
var xhr = new XMLHttpRequest();
  var onFufillment = [];
  var onError = [];
  var onCompletion = [];
  var method = "GET" || options.method;
  xhr.onreadystatechange = function () {
    var _data = this;
    if (this.readyState == 4 && this.status == 200) {
      // Action to be performed when the document is read;
      onFufillment.forEach(function (callback) {
          callback(_data);
      });
     onCompletion.forEach(function (callback) {
        callback(_data);
      });
    } else if (this.readyState == 4 && this.status !== 200) {
      onError.forEach(function (callback) {
        callback(_data);
      });
      onCompletion.forEach(function (callback) {
        callback(_data);
      });
    }
  };
  xhr.open(method, url, true);
  xhr.send();


	return {
    	then: function then(fufillmentFunction) {
          onFufillment.push(fufillmentFunction);
          return this;
   		},
    	catch: function _catch(errorFunction) {
      	  onError.push(errorFunction);
      	  return this;
      },
        finally: function _finally(completionFunction) {
         onCompletion.push(completionFunction);
         return this;
    }
  };
}

Использование API будет выглядеть так:

var futureData = fetch('


futureData.then(function(data){
  console.log(data)
}).then(function(response){
  console.log(response);
}).catch(function(error){
  console.log(error);
});

Что это сделало? Единственное отличие во второй итерации заключалось в then, catch and finally куда я только что вернулся this что означает, что каждая функция возвращается сама, что позволяет ее связать (частично).

Лучше да? Но можем ли мы сделать лучше этого? Конечно, можем. Возвращенный объект можно поместить в прототип функции, чтобы мы могли сэкономить память в ситуации, когда функция используется несколько раз.

Вот как это выглядит на третьей итерации:

"use strict";
function fetch() {
  var fetchMethod = Object.create(fetch.prototype);
  var url = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : '';
  var options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
var xhr = new XMLHttpRequest();
  fetchMethod.onFufillment = [];
  fetchMethod.onError = [];
  fetchMethod.onCompletion = [];
  var method = "GET" || options.method;
  xhr.onreadystatechange = function () {
    var _data = this;
    if (this.readyState == 4 && this.status == 200) {
      // Action to be performed when the document is read;
      fetchMethod.onFufillment.forEach(function (callback) {
          callback(_data);
      });
     fetchMethod.onCompletion.forEach(function (callback) {
        callback(_data);
      });
    } else if (this.readyState == 4 && this.status !== 200) {
      fetchMethod.onError.forEach(function (callback) {
        callback(_data);
      });
      fetchMethod.onCompletion.forEach(function (callback) {
        callback(_data);
      });
    }
  };
  xhr.open(method, url, true);
  xhr.send();
  return fetchMethod;
};
fetch.prototype.then = function(fufillmentFunction) {
      this.onFufillment.push(fufillmentFunction);
      return this;
};
fetch.prototype.catch = function(errorFunction) {
      this.onError.push(errorFunction);
      return this;
};
fetch.prototype.finally = function(completionFunction) {
      this.onCompletion.push(completionFunction);
      return this;
};

Таким образом, эта версия в основном перемещает возвратную функцию в прототип выбора. Если вы не понимаете утверждения, я рекомендую ознакомиться с этой статьей о прототипе Javascript (спасибо, Тайлер МакГиннис).

Это улучшение? Да!!! Можем ли мы улучшить? Конечно, можем. Мы можем использовать new ключевое слово для нашего преимущества здесь и удалить явный оператор return.

Следующая итерация будет выглядеть так:

"use strict";
function Fetch() {
  var url = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : '';
  var options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
  var xhr = new XMLHttpRequest();
  this.onFufillment = [];
  this.onError = [];
  this.onCompletion = [];
  var method = "GET" || options.method;
  var internalFetchContext = this;
  xhr.onreadystatechange = function () {
    var _data = this;
    if (this.readyState == 4 && this.status == 200) {
      // Action to be performed when the document is read;
      internalFetchContext.onFufillment.forEach(function (callback) {
          callback(_data);
      });
     internalFetchContext.onCompletion.forEach(function (callback) {
        callback(_data);
      });
    } else if (this.readyState == 4 && this.status !== 200) {
      internalFetchContext.onError.forEach(function (callback) {
        callback(_data);
      });
      internalFetchContext.onCompletion.forEach(function (callback) {
        callback(_data);
      });
    }
  };
  xhr.open(method, url, true);
  xhr.send();
};
Fetch.prototype.then = function(fufillmentFunction) {
      this.onFufillment.push(fufillmentFunction);
      return this;
};
Fetch.prototype.catch = function(errorFunction) {
      this.onError.push(errorFunction);
      return this;
};
Fetch.prototype.finally = function(completionFunction) {
      this.onCompletion.push(completionFunction);
      return this;
};

Позвольте объяснить изменения:

  • Изменено название функции с fetch на Fetch, это просто условность при использовании new ключевое слово
  • Поскольку я использую new ключевое слово я могу затем сохранить разные массивы, созданные до this контекст.
  • Поскольку функция перешла в onreadystatechange имеет собственный контекст, мне пришлось сохранить оригинал this в собственную переменную, чтобы я мог вызвать ее в функции (я знаю, this может раздражать)
  • Преобразованы функции прототипа в новое название функции.

Использование будет выглядеть так:

var futureData = new 

Fetch('
futureData.then(function(data){
  console.log(data)
}).then(function(response){
  console.log(response);
}).catch(function(error){
  console.log(error);
})

Вуаль! Это было очень весело. Но можем ли мы улучшить? Конечно, можем.

Но я оставлю вам это. Я хотел бы увидеть вашу собственную реализацию API в комментариях ниже.

Если вам понравилась статья (и даже если бы вам не понравилось), я был бы признателен за хлопок (или 50) от вас. Спасибо.

Добавить комментарий

Ваш адрес email не будет опубликован.