Введение в fetch

http://updates.html5rocks.com/2015/03/introduction-to-fetch
  • Перевод

Прощай, XMLHttpRequest!


fetch() позволяет вам делать запросы, схожие с XMLHttpRequest (XHR). Основное отличие заключается в том, что Fetch API использует Promises (Обещания), которые позволяют использовать более простое и чистое API, избегать катастрофического количества callback'ов и необходимости помнить API для XMLHttpRequest.


Fetch API стал доступен пользователям вместе с Service Worker'ами в global скоупе в Chrome 40, однако уже в версии 42 он станет доступен в скоупе window. Разумеется, для всех остальных браузеров, которые пока ещё не поддерживают fetch существует полифил от GitHub, который доступен уже сегодня.

Простой Fetch запрос


Давайте начнём со сравнения простого примера, реализованного с XMLHttpRequest и fetch. Всё, что мы будем делать в этом примере — сделаем запрос на URL, получим ответ и распарсим его как JSON.

XMLHttpRequest


Пример с XMLHttpRequest потребует от нас установить два обработчика событий на success и error, а так же вызвать два метода: open() и send(). Пример из MDN документации:

function reqListener() {  
  var data = JSON.parse(this.responseText);  
  console.log(data);  
}

function reqError(err) {  
  console.log('Fetch Error :-S', err);  
}

var oReq = new XMLHttpRequest();  
oReq.onload = reqListener;  
oReq.onerror = reqError;  
oReq.open('get', './api/some.json', true);  
oReq.send();

Fetch


Наш fetch запрос будет выглядеть так:
fetch('./api/some.json')  
  .then(  
    function(response) {  
      if (response.status !== 200) {  
        console.log('Looks like there was a problem. Status Code: ' +  
          response.status);  
        return;  
      }

      // Examine the text in the response  
      response.json().then(function(data) {  
        console.log(data);  
      });  
    }  
  )  
  .catch(function(err) {  
    console.log('Fetch Error :-S', err);  
  });


Первым делом мы проверяем статус ответа и проверяем, успешно ли выполнился запрос (ожидаем 200 статус). Если всё хорошо, то парсим ответ как JSON.

Ответом fetch() является Stream-объект. Это означает, что в результате вызова метода json() мы получим Promise, т.к. чтение из подобного объекта является асинхронным.

Метаданные ответа


В предыдущем примере мы изучили, как можно проверить статус объекта ответа и конвентировать сам ответ в JSON. Остальные метаданные, к которым вы возможно получить доступ (например, заголовки), приведены ниже:

fetch('users.json').then(function(response) {  
    console.log(response.headers.get('Content-Type'));  
    console.log(response.headers.get('Date'));

    console.log(response.status);  
    console.log(response.statusText);  
    console.log(response.type);  
    console.log(response.url);  
});

Типы ответа


Когда мы делаем fetch-запрос, ответу будет дан тип «basic», «cors» или «opaque». Эти «типы» указывают на то, с какого ресурса пришли данные и могут быть использованы для того, чтобы определить процесс обработки данных.

Когда запрос сделан на ресурс, находящимся на том же origin (имеется ввиду, что запрос выполняется в рамках одного сайта. прим. пер.), ответ будет содержать тип «базовый» и для такого запроса не будет никаких ограничений.

Если запрос сделан с одного origin'а на другой (кроссдоменный запрос), который, в свою очередь, вернул CORS заголовки, тогда типом будет являться «cors». Объекты с типами «cors» и «basic» почти идентичны, однако «cors» несколько ограничивает метаданные, к которым может быть получен доступ до «Cache-Control», «Content-Language», «Content-Type», «Expires», «Last-Modified», и «Pragma».

Что касается «opaque» — то он приходит в случаях, когда выполняется CORS запрос, но удаленный ресурс не возвращает CORS заголовки. Данный тип запроса не предоставляет доступ данным или заголовку статуса, поэтому мы не имеем возможности судить о результате выполнения запроса. В рамках текущей имплементации fetch() не представляется возможности выполнять CORS запросы из скоупа window, и вот здесь написано почему. Эта функциональность должна быть добавлена, как только Cache API станет доступным из объекта window.

Вы можете определить ожидаемый режим запроса, тем самым фильтруя результаты запросов с неподходящим типом. Режим запроса может быть установлен на следующий:

— “same-origin” успешно выполняется только для запросов на тот же самый origin, все остальные запросы будут отклонены.
— “cors” работает так же, как «same-origin» + добавляет возможность создавать запросы к сторонним сайтам, если они возвращают соответствующие CORS заголовки.
— “cors-with-forced-preflight” работает так же, как «cors», но перед запросом всегда отсылает тестовый запрос на проверку.
— “no-cors” используется, когда необходимо выполнить запрос к origin, который не отсылает CORS заголовки и результатом выполнения является объект с типом «opaque». Как говорилось выше, в данный момент это невозможно в скоупе window.

Чтобы определить режим запроса, добавьте объект опций вторым параметром к запросу и установите «mode» в этом объекте:

fetch('http://some-site.com/cors-enabled/some.json', {mode: 'cors'})  
  .then(function(response) {  
    return response.text();  
  })  
  .then(function(text) {  
    console.log('Request successful', text);  
  })  
  .catch(function(error) {  
    log('Request failed', error)  
  });

Цепочки Promises


Одной из прекрасных особенностей Promise'ов является возможность группировать их в цепочки. Если говорить о них в скоупе fetch(), то они позволяют нам «шарить» логику между запросами.

Если вы работаете с JSON API, вам потребуется проверить статус и распарсить JSON для каждого ответа. Вы можете упростить свой код, определив парсинг статуса и JSON как раздельные функции, которые вернут Promise'ы. Вам останется подумать только об обработке самих данных и, разумеется, исключений.

function status(response) {  
  if (response.status >= 200 && response.status < 300) {  
    return Promise.resolve(response)  
  } else {  
    return Promise.reject(new Error(response.statusText))  
  }  
}

function json(response) {  
  return response.json()  
}

fetch('users.json')  
  .then(status)  
  .then(json)  
  .then(function(data) {  
    console.log('Request succeeded with JSON response', data);  
  }).catch(function(error) {  
    console.log('Request failed', error);  
  });

Мы определяем функцию, которая проверяет response.status и возвращает результат: Promise.resolve() или Promise.reject(). Это первый вызванный метод в нашей цепочке, и если он успешно завершается(Promise.resolve()), то вызывается следующий за ним метод — fetch(), который, в свою очередь, опять возвращает Promise от response.json(). После этого вызова, в случае удачного выполнения, у нас будет готовый JSON объект. Если парсинг провалится, Promise будет отменен и сработает условие возникновения исключения.

Но самое лучшее здесь — это возможность переиспользовать такой код для всех fetch-запросов в приложении. Такой код проще поддерживать, читать и тестировать.

POST запрос


Уже давно никого не удивишь необходимостью использовать POST метод с передачей параметров в «теле» запроса для работы с API.
Чтобы осуществить такой запрос, мы должны указать соответствующие параметры в объекте настроек fetch():

fetch(url, {  
    method: 'post',  
    headers: {  
      "Content-type": "application/x-www-form-urlencoded; charset=UTF-8"  
    },  
    body: 'foo=bar&lorem=ipsum'  
  })
  .then(json)  
  .then(function (data) {  
    console.log('Request succeeded with JSON response', data);  
  })  
  .catch(function (error) {  
    console.log('Request failed', error);  
  });

Посылаем учётные данные через Fetch-запрос


Если вы хотите отправить запрос с каким-либо учётными данными (например, с cookie), вам следует установить `credentials` в опциях запроса на «include»:

fetch(url, {  
  credentials: 'include'  
})

FAQ


Могу ли я отменить fetch() запрос?
В настоящий момент это невозможно, но это активно обсуждается на GitHub

Существует ли полифил?
Да

Почему «no-cors» реализован для service workers, но не для window?
Это было сделано из соображений безопасности. Подробнее можно ознакомиться здесь.
Метки:
Поделиться публикацией
Похожие публикации
Комментарии 145
  • +6
    Могу ли я отправить/получить файл?
    Могу ли я следить за прогрессом выполнения запроса?
    Могу ли я сделать синхронный запрос?
    • 0
      Судя по ссылке «Да» в посте — можно отправить файл :)
      • –3
        body: Any body that you want to add to your request: this can be a Blob, BufferSource, FormData, URLSearchParams, or USVString object. Note that a request using the GET or HEAD method cannot have a body.

        developer.mozilla.org/en-US/docs/Web/API/GlobalFetch/fetch
        Зачем вам синхронный запрос?
        • +12
          Зачем вам синхронный запрос?
          На глупый вопрос глупый ответ. Синхронный запрос нужен чтоб запрос делался синхронно.
          • +4
            То есть есть мифическая ситуация, в которой на клиенте нужен лок потока выполнения, ради выполнения запроса к серверу?
            • –1
              Почему мифическая?
              Вы совершенно исключаете, что могут потребоваться получить/отправить такие данные, без которых не должно происходить никаких действий пользователя с интерфейсом. Если это делать асинхронным запросом, придется городить костыли.
              • –3
                Любой лок интерфейса отпугнет пользователя, так что развлекайте его пока идет запрос.
                • +4
                  Пугливость пользователя и юзабилити — это вопросы другой плоскости. Мы говорим про технические возможности.
                  • +8
                    ну… чисто теоретически это может быть даже не пользовательское приложение… И даже без Гуя…
                    • +1
                      Представил, как диспетчер (у нас веб-интерфейс), что-то там испугался и убежал с рабочего места в поисках развлечений :-D

                      Веб это не толь «сайтики».
                    • +2
                      Можете привести простой пример, когда возникает необходимость отправить запрос синхронно и блокировать поток обработки UI до ответа онного? И чем такое решение лучше, чем асинхронный запрос?
                      • 0
                        Когда блокировка гуя не критична или гуя вообще нет. Но при этом нужно сделеть:
                        — обращение к какому-нибудь API обернутое просто в функцию которая просто возвращает значение, без нагромождения коллбэков или
                        — запросы в определенной последовательности или даже в цилке, без мороки с deffered или
                        — в конце концов когда запрашивается модальное окно, то весь вьюпорт браузера обычно закрывают каким-нибудь полупрозрачным слоем, иногда с анимированным прелоадером. Что это как не имитация блокировки пользовательского интерфейса?
                        • +6
                          — в конце концов когда запрашивается модальное окно, то весь вьюпорт браузера обычно закрывают каким-нибудь полупрозрачным слоем, иногда с анимированным прелоадером. Что это как не имитация блокировки пользовательского интерфейса?

                          Да, но там еще бывает кнопка «Отмена» или «Закрыть». Может быть еще анимация или полезные советы.
                          А в вашем случае будет только
                          висящее окно браузера



                          — обращение к какому-нибудь API обернутое просто в функцию которая просто возвращает значение, без нагромождения коллбэков или
                          — запросы в определенной последовательности или даже в цилке, без мороки с deffered


                          По-моему, уже давно существует тысяча и один способ нормально работать и с callback-ами и с promise-ами и даже с колбасными или кисломолочными изделиями.
                          • –5
                            Да, подвисание браузера не всегда хорошо, даже почти всегда не хорошо. Можно сделать юзабельнее. Прекрасно это понимаю.
                            Да, есть возможность заменить синхронные запросы с помощью асинхронных. И есть библиотеки которые делают удобно.

                            Является ли это достаточными причинами, выпиливать, точнее недореализовывать, в нативной поддержке браузером инструмента, который был до этого и даже использовался?

                            Я считаю, что нет, не достаточно, синхронные запросы должны быть. Оппоненты считают, что да — достаточно, инструмент этот мало востребован и заменяем, они не могут придумать для чего это надо, значит его быть не должно.

                            Я считаю, что я ближе к правоте, посколько сюда тянутся воспросы обратной совместимости, переносимости.

                            Да и вообще, наличие инструмента лучше, чем его отстутствие. Разумеется, с поправкой на то, что реализация этого инстремента не ухудшает браузер. Я не думаю, что Хром станет хуже работать, жрать больше памяти и т.п,. если будет реализованы синхронные запросы.
                            • +6
                              для обратной совместимости есть старый добрый XMLHttpRequest, его никто не выкинул, а тут запилили новое апи, всё такое на обещаниях и асинхронное. Если сюда добавить синхронность в чем тогда смысл делать новое апи? тоже самое (promises) можно получить и сейчас в том же jquery
                              • +5
                                >> Да и вообще, наличие инструмента лучше, чем его отстутствие.

                                Далеко не всегда. Если инструмент в 90% случаев будут использовать некорректно (т.е. писать синхронный код там, где на самом деле нужен асинхронный, просто потому, что так проще, или потому, что автор увидел синхронный метод первым), то его может быть лучше убрать. Или, по крайней мере, спрятать подальше.
                              • +1
                                Кстати, изделие не «кобласное», а «англо-средневековое». :)
                            • –4
                              Ну к примеру у нас есть веб-приложение (SPA), которое в самом начале загрузки должно получить конфиг с сервера, без которого ни один последующий запрос не может выполняться (в конфиге содержатся сопутствующие данные), ни интерфейс не может отрисоваться (в конфиге содержатся языковые литералы и другие настройки интерфейса). Вот мы и делаем синхронный запрос на получения конфига, а в это время крутится прелоудер.

                              Если можете предложить более простой способ решения задачи, то с удовольствием прочитаю.

                              А вообще вопрос на мой взгляд ни о чем. Причин делать синхронные запросы ровно столько же, сколько и асинхронные. Все дело в решаемой задаче.

                              Ещё пример: нужно последовательно выполнить несколько запросов к серверу. Разве это не кейс для использования синхронных запросов?
                              • +4
                                Во всех перечисленных случаях прекрасно подойдут асинхронные запросы.
                                • –2
                                  Пусть так, тогда могли бы вы подробнее объяснить в чем прелесть решать синхронную задачу асинхронным методом?
                                  • +2
                                    js — однопоточный язык. В нем попросту нельзя использовать ничего синхронного.

                                    Была бы возможность создать отдельный поток и делать синхронные запросы в нем — не было бы никаких проблем с синхронными запросами. Но возникли бы проблемы с синхронизацией потоков и общим доступом к разделяемым ресурсам — потому от дополнительных потоков и отказались.
                                    • 0
                                      А какая разница однопоточный или нет? Если задача требует блокировки потока, почему я не должен использовать синхронный запрос?
                                      • +1
                                        Сегодня вам требуется блокировка потока — а завтра понадобится сделать еще одну цепочку вызовов параллельно. Сразу написать на промизах так же просто, как и написать запросы синхронно — а вот переделывать синхронный код на промизы может оказаться сложно.
                                        • 0
                                          Зато на fibers переделывать синхронный код легко и просто, но их почему-то не внедряют в браузеры :-(
                                          • –1
                                            Это уже мои риски по рефакторингу. И никак не должно влиять на наличие или отсутствие возможности в самом API. Задачи бывают разные и ни я, ни вы не можем точно описать их множество. Поэтому не вижу ничего плохого в том, что возможность синхронного запроса останется.
                                      • 0
                                        А минусующие могут как-то мысль высказать? Даже если человек не прав, смысл тупо минус ставить? Объясните своё мнение.
                                        • –4
                                          Объяснение простое — промисы сейчас в тренде.
                                          Ныне миром программирования правят именно тренды. Никому не надо думать, искать лучшее решение, взвешивать плюсы и минусы разных подходов. Сейчас все стараются бежать туда, куда бежит большинство. Пусть даже бегут они в пропасть, но бегут вместе и из пропасти потом будут выбираться тоже вместе. А если бежать самостоятельно, то есть ненулевой шанс тоже оказаться в пропасти, но в которой кроме тебя почти никого не будет и нельзя будет «найти ответ на stackoverflow» или «подключить библиотеку, которая решает проблему, созданную другой библиотекой».

                                          Ещё вчера, все бежали в сторону (X)(HT)ML и ставили минусы всем, кто не закрывал теги. Сейчас же ставят минусы за использование XML в апи, вместо трендового JSON. Промисы тоже скоро выйдут из моды благодаря генераторам и тому подобным реализациям кооперативной многозадачности. Но для длительных операций использование промисов, генераторов и прочих «ждём до завершения» штук — плохое решение, так как исключает возможность следить за прогрессом выполнения задачи. Именно поэтому при грамотной архитектуре fetch должен возвращать не объект Promise, а объект XMLHttpRequest, который уже может реализовывать thenable интерфейс для частного случая «ждём до завершения».
                                          • –3
                                            По большому счёту я с вами согласен. Что косвенно доказывает и собираемые нами минусы. Ну что ж, пусть я и буду выглядит как аутсайдер, но не устану повторять: каждой задаче своё решение. И если задача подразумевает синхронное решение, то такая возможность должна присутствовать в API.
                                            • +1
                                              для синхронных запросов есть XMLHttpRequest если нужно используйте, смысла тащить это в новое api нет
                                              • +2
                                                Вы же понимаете, что до появления многоядерности — не было вообще реально параллельных задач? А значит любую задачу можно назвать синхронной. Однако всех устраивает, что операционные системы дают возможность создания процессов, потоков. Языки и виртуальные машины — абстрагирование через события и т.д.
                                                И пока вы не аргументировали, с чего это запрос к внешнему серверу (по сути неконтролируемая нашей системой операция) должен быть синхронным, кроме мнимого удобства программирования (хотя это неправда, ведь любое изменение логики повлечет за собой необходимость менять весь ваш загрузчик).
                                                • –3
                                                  А как по-другому гарантировать, что ни одна строчка кода, ни в одном из последующих файлов не выполняться, пока не придёт ответ на первичный запрос к серверу? Если вы поведаете более простое и железо-бетонное решение этой задачи, буду признателен.
                                                  • +3
                                                    В JS нет понятия «последующие файлы». Я вообще с трудом понимаю о чем речь, если о теге script в html (который к файлам отношения никакого не имеет), то советую просто и железно-бетонно ознакомиться с модулями.
                                                    Асинхронность никак не связана с потерей последовательности выполнения — это решается на уровне логики вашего приложения в любом языке.
                                                    • –4
                                                      Вы так и не ответили на вопрос и модули тут не причём. Да и логика приложения может быть упакована как угодно. Если не можете дать конкретное решение задачи, зачем вообще отвечать?
                                      • +4
                                        >> Ещё пример: нужно последовательно выполнить несколько запросов к серверу. Разве это не кейс для использования синхронных запросов?

                                        Это как раз кейс для промисов — строите из них нужную цепочку, вот вам и последовательность.
                                        • –2
                                          Промисы тут тоже подойдут, но не понимаю почему все отвергают синхронное решение синхронной задачи? Вы можете пояснить почему синхронные запросы здесь будут хуже чем асинхронные с промисами?
                                          • +1
                                            Основная причина — синхронные запросы, если их сделать, будут использоваться не только в таких случаях, но и в других, где на самом деле нужна асинхронность, просто потому, что их проще использовать (а быдлокодеров, которые кодят по шаблонам по принципу «что короче и понятнее», куда больше, чем тех, кто будет разбираться).

                                            А в GUI-приложении, таких случаев на самом деле куда больше, чем тех, где синхронность пойдет. Поэтому, если оставшиеся в меньшинстве случаи можно адекватно покрыть асинхронностью, даже за счет небольшого усложнения кода, то лучше оставить только асинхронные API.

                                            Ну и в вашем конкретном примере, все же может потребоваться выполнять какой-то JS, когда остальное грузится. Анимированный индикатор прогресса, положим, вы сделаете на CSS, а вот какие-нибудь хитрые таймауты — уже нет.
                                            • 0
                                              Анимированный индикатор прогресса, положим, вы сделаете на CSS, а вот какие-нибудь хитрые таймауты — уже нет.

                                              Да и с CSS нет никаких гарантий, что страница тупо не будет «висеть».
                                              • 0
                                                Это уже от реализации JS в браузере зависит, нет? В принципе нет никаких причин, по которым JS должен крутиться на том же потоке, что и рендеринг, и обработка ввода.
                                                • 0
                                                  Это уже от реализации JS в браузере зависит, нет? В принципе нет никаких причин, по которым JS должен крутиться на том же потоке, что и рендеринг, и обработка ввода.

                                                  Да 100%, я к тому, что слишком много нюансов ради непонятного использования синхронного запроса.
                                                  • 0
                                                    Покажите-ка мне пожалуйста браузер, в котором JS крутится в «отдельном» потоке от GUI? (Разумеется, не берем в рассчёт веб-воркеры)
                                                    • 0
                                                      12 опера, в некотором роде, так и делает. GIF анимации продолжают работать, страница скролится. Проверил только что. В остальном поведение то же, что и в других браузерах: текст не выделяется и пр.
                                                      • 0
                                                        А каким образом это соотносится? Для меня отображение GIF и скролл страницы не определяет то, что JS работает в отдельном треде.
                                                        • 0
                                                          Ну Chromium и этого не предоставляет :) Loading-анимации зависают, и, тем самым, перестают выполнять свою задачу.
                                                          • 0
                                                            Вот именно эту проблему и стоит решать в первую очередь, а не реализовывать новый апи, с менее богатым функционалом.
                                                            • +2
                                                              Да и эту проблему, если честно, решать тоже не обязательно, а уж тем более в первую очередь. Лучше заняться ES7, с его await, чтобы писать асинхронный код можно было без боли :D (пока что на это претендуют только всякие извращения с генераторами, которые вообще, как мне кажется, не для этого были созданы).
                                                              • –2
                                                                Лучше всё же fibers чем бесконечные await-ы
                                                                • +1
                                                                  Чем лучше?
                                                                  • –1
                                                                    «чтобы писать асинхронный код можно было без боли»
                                              • –2
                                                Мне кажется, что ваши опасения это все же перестраховки. Мне не доводилось отмечать рьяное использование синхронных запросов в XMLHttpRequest, да и навряд ли «быдлокодеры» настолько подробно будут изучать документацию, чтобы обратить внимание на какой-нибудь небольшой флажок, позволяющий включить синхронность.

                                                GUI или нет, у меня логика простая — есть задачи асинхронные и асинхронные с признаками синхронности (тут промисы и все такое), а есть чисто синхронные. И если честно, не понимаю зачем исключать эту возможность совсем.

                                                p/s Глянул в код нашего приложения, чтобы освежить в памяти и представил эту его часть на промисах. Ох, как бы там все криво стало из-за этого. Да и цепочку не всегда удается сделать, потому что код, который должен выполняться синхронно, часто в разных классах и даже файлах лежит. Рефакторинг всего этого добра в цепочку промисов обойдется очень дорого при непонятном профите.
                                                • +2
                                                  Я не веб-программист, поэтому мне тяжело сказать конкретно насчет XMLHttpRequest. Но в гуевых приложениях чрезмерное использование синхронных API и игнор асинхронных исторически являются огромной проблемой, из-за которой вы видите пресловутый «бублик» ожидания куда чаще, чем надо. В WinRT, кстати, именно из-за этого практически все API сделали асинхронными без синхронных вариантов, по принципу «не умеешь — научим, не хочешь — заставим».

                                                  Асинхронность сама по себе ортогональна последовательному выполнению, кстати. В этом смысле промисы не «сихрноннее» обычных коллбэков. Просто с ними проще делать композицию — не обязательно последовательную.

                                                  Насчет кривости кода — это во многом фактор языка. В C# писать асинхронный код стало буквально в разы проще с появлением async/await, который позволяет размотать лапшу из continuation-лямбд в нормальный код, который выглядит абсолютно как синхронный, за исключением этого самого await в точках передачи управления. В ES6 подобное тоже планируется.
                                                  • 0
                                                    Никто и не говорит о чрезмерном использовании синхронности. Речь о том, что выпиливать эту возможность совсем означает заведомо заставлять решать синхронные задачи или задачи требующие блокировки потока, с помощью костылей. А зачем? Кейс про кривизну рук, ну это уже слишком притянуто.
                                                  • +1
                                                    А мне вот доводилось видеть $.ajaxSetup({async:false}) в коде нашего приложения. На вопрос, что это, веб-программист ответил, что «иначе ничего не работает».
                                                    • –2
                                                      Ну и что? Я раньше на полюсах писал и столько мне доводилось видеть говно-кода связанного с тамошними темплейтами и уж там все это кривой было сделано.

                                                      Однако, не смотря на то, что я считаю, что темплейты в си++ могли бы быть реализованы более внятным образом и что на действительно красиво их «варить» умеют далеко не все. Но я никогда и думать не думал, что надо их выпилить из языка, только потому что их неправильно используют.

                                                      Вам не кажется, что это уж слишком радикально? С синхронными запросами также история.
                                            • 0
                                              У нас тоже есть веб-приложение, которое должно получить конфиг и подгрузить данные в самом начале. У нас показывается текст, типа «Refreshing data, please wait...». Как только подгрузили, показываем основной интерфейс. Зачем тут синхронный запрос я не оч понимаю. Да и грузится все не одним запросом, а где-то 5 (разные ресурсы), т.ч. еще и быстрее получается за счет параллельности.
                                              • 0
                                                Я полностью согласен с mayorovp и int19h, но позвольте мне спросить: в чём проблема инкапсулировать данные в страницу, которую вы отдаете с сервера? Так или иначе вы отдаете хотя бы index-файл с базовой разметкой с сервера, так пусть он сразу содержит данные, которые вам будут нужны при инициализации, не?
                                                • 0
                                                  html можно намертво зекешировать, чтобы отдавался мгновенно.
                                                  • 0
                                                    Кстати, а ведь все данные, нужные при сразу после загрузки, можно загружать через статический же тег script…
                                                    • 0
                                                      Конечно, но это костыль.
                                                      • 0
                                                        пользователь может открывать ваш сайт несколько раз, то есть открыв его второй третий и т.п. раз от опять будет скачивать 1мб шаблонов, в общем кроме крутящегося кружочка для интернет приложений лучше в таком случае ничего не отдавать в «index.html»
                                                        Кстати fetch api презентовано внутри Service Worker которые как раз для сложных случаев кастомного кеширования.
                                                        • 0
                                                          Почему это вы считаете, что шаблоны не будут закешированы браузером — и он будет скачивать их каждый раз?..
                                                          • 0
                                                            потому что вы хотя бы год в футере да меняете или часть данный в index.html передаете.
                                                            • 0
                                                              Ну так скачивать новый шаблон раз в год и скачивать новый шаблон каждый раз — это же немного разные вещи?..
                                                              • 0
                                                                Простите за лир. отступление, но есть ещё люди, которые год делают не через решения наподобе new Date().getFullYear()?
                                                      • 0
                                                        Вы так говорите, как будто закешировать страницу с данными невозможно :)
                                                        • –4
                                                          Данные имеют свойство меняться.
                                                          • 0
                                                            Ну тогда инвалидируйте кеш, в чём проблема?
                                                            • –2
                                                              Расскажите как вы будете инвалидировать этот кэш без запроса к серверу
                                                              • 0
                                                                При следующем запросе данной страницы с сервера я обязательно проверю кеш, а скорее всего, сделаю это воркером, который будет работать на сервере. Но мне кажется я понял, что вы имеете ввиду: если я перейду на др. страницу внутри SPA и вернусь обратно — данные не изменятся. Но тут зависит исключительно от вашей реализации. Хотите — не изменятся, хотите — изменятся, предзагрузка данных не влияет на логику приложения.
                                                                • 0
                                                                  Как вы «проверите кеш» или «запустите воркер», если страница ещё не загружена? А если уже загружена, то она уже взята из кэша с возможно устаревшими данными.
                                                                  • 0
                                                                    Вы правда не знаете, как работают заголовки HTTP, управляющие кешированием?
                                                                    • 0
                                                                      Напомню, с чего начался этот тред:

                                                                      html можно намертво закешировать, чтобы отдавался мгновенно.
                                                                      • 0
                                                                        Его можно намертво закешировать на день/неделю/до нового года — а по истечении этого периода просто сообщать браузеру, что файл все еще не изменился (если это действительно так).
                                                • 0
                                                  Когда нужно реализовать SCORM API. Оно синхронное по спеке. Выбора нет.
                                              • +6
                                                я делал синхронные запросы для старых браузеров в unload, чтобы они таки доходили до сервера, так что ситуация не очень мифическая.
                                                возможно человек ноду использует, хотя там были либы чтобы делать такие локи для асинхронных штук
                                                • +1
                                                  Ели вам нужно было отправить на сервер какую-то статистику перед тем как пользователь покинет страницу, то можно подписаться на события load и error у вновь созданного изображения. И не забыть про таймаут.
                                                  • 0
                                                    если вы про отправить данные в урле через new Image, то это работает только если вы вкладку закрыли, а если весь браузер то уже не успевает
                                                    • 0
                                                      В случае с new Image, запросы оборвутся сразу как пользователь решит покинуть страницу.
                                                      И ни unload ни beforeunload не помогут дождаться окончания выполнения запроса.

                                                      Разве что beforeunload и то до того момента пока пользователь не решить нажать кнопку «Покинуть страницу»
                                                  • +2
                                                    На мой взяглд, несколько странный подход. А если данных будет оч. много, вы заставите пользователя ждать? Я пробыл на сайте 1 час и уходя у меня накопился 1Мб данных, которые надо отправить на сервер. Предположим, я нахожусь на даче и использую 3G телефона, который берёт «на добром слове». Вы заблокируете мне весь браузер, пока я не передам 1Мб по 3G? Вы маньяк!

                                                    Разнообразные «тепловые карты», отслеживающие посетителя на сайте и т.п. посылают данные отнюдь не в момент закрытия вкладки.
                                                    • 0
                                                      Т.к. я сам отправляю запрос то я знаю сколько данных я отправляю — и да вы ошиблись где-то на 3-6 порядков с 1мб и 10мб соответственно. Да и что можно отправлять в качестве статистики на 1мб я даже представить не могу.
                                                      У XmlHttpRequest есть замечательное свойство timeout которое можно поставить в 250мс например и пользователь этого не заметит, даже если запрос отвалится по таймауту (штатное время ответа до 1мс, остальное только сетевые задержки)
                                                      • +1
                                                        ну и на всякий случай — где возможно у меня используется developer.mozilla.org/en-US/docs/Web/API/Navigator/sendBeacon
                                                        с выходом 39 версии хрома это поддерживают уже больше 70% настольных браузеров
                                                        • 0
                                                          Разумеется, я взял цифру с количеством статистики с потолка, тут извините (т.к. никогда не занимался разработкой софта с подобной функциональностью). Но как вы верно подметили, бóльшую часть времени занимает транспортировка пакетов и обработка данных, поэтому невозможно прогнозировать, с какой скоростью вернётся ответ. Если вы ожидаете этого ответа перед закрытием, то вы «задерживаете» пользователя на непрогнозируемый период времени.
                                                    • –3
                                                      Элементарный пример — посыл запроса перед закрытием окна браузера по событию beforeunload.
                                                      • +2
                                                        Т.е. бизнес-логика такая
                                                        «Подвесить пользователю браузер с целью (?) сохранения каких-то данных безо всяких гарантий их сохранения (процесс можно убить, выключить компьютер и т.д.»
                                                        И в чем же сакральный смысл сего действия?
                                                        • –1
                                                          У меня был проект, где перед закрытием окна надо было освободить подписку на серверные события, потому что там был лимит на 30 штук. И если этого не делать — то они быстро кончались. Если процесс умер — бог с ним, но по возможности — почему бы не прибрать за собой?
                                                          • –1
                                                            А чтоб долго не ждать можно таймаут поставить, мне же по большому счету все равно, каков результат, мне надо сервер пнуть, чтоб тот подписку освободил, а для этого достаточно, чтоб запрос на него просто попал и будь что будет.
                                                            • +2
                                                              В таком случае вы можете «пнуть» сервер асинхронным запросом.
                                                              P.S. Что значит «освободить подписку на серверные события»?
                                                              • –1
                                                                Асинхронный запрос в таком случае убивается моментально, до сервера не доходит. Освободить подписку значит сказать серверу, чтобы тот перестал пушить события по данной подписке.
                                                                • +1
                                                                  Т.е. бизнес-процесс такой (если я правильно поняла):
                                                                  На сервере есть некий сервис, который для каждой открытой страницы куда-то (в память?) постоянно сохраняет какие-то новые данные. И они постоянно копятся с единственной возможностью на освобождение ресурсов — повесив пользователю браузер? А если он убьет процесс, то, видимо, вовсе до перезагрузки сервера?
                                                                  • –2
                                                                    Есть сервер, на нем происходят некие события (упрощенно — меняется онлайн статус людей, им присылают сообщения, их сообщения меняют статус и т.д.), сервер по запросу клиента создает трекинг этих событий и отправляет клиенту пуш-нотификации. Если клиент допустим 5 минут не присылал хартбиты, то подписка удаляется, но также есть возможность удалить ее принудительно, если клиент считает, что в ней нет надобности. Это сугубо опциональная процедура, однако, если клиент начинает держать слишком много подписок — сервер это пресечет. Поэтому в интересах клиента ее уничтожить, если клиент 100% знает, что подписка более не нужна.

                                                                    «Повесив пользователю браузер» — 200мс при закрытии страницы никто никогда не заметит. Если дольше — то случится таймаут и страница все равно закроется.

                                                                    Какие далеко идущие выводы — до перезагрузки сервера… Смешно )
                                                                    • –1
                                                                      Если клиент допустим 5 минут не присылал хартбиты, то подписка удаляется, но также есть возможность удалить ее принудительно, если клиент считает, что в ней нет надобности.

                                                                      Это сугубо опциональная процедура, однако, если клиент начинает держать слишком много подписок — сервер это пресечет.

                                                                      Так в чем смысл этой «отписки»? В том, что сервер не может обеспечить хранение всех подписок по заявленному алгоритму (в течении 5 минут)? Бесполезная трата ресурсов (и клиента, и сервера) на ручную отписку (раз автоматическая все равно будет происходить)?
                                                                      Ключевой момент — отсутствие гарантий, а значит бессмысленность.

                                                                      Какие далеко идущие выводы — до перезагрузки сервера… Смешно )

                                                                      Это была экстраполяция из ваших аргументов. Видите — вам тоже смешно)
                                                                      • –1
                                                                        Сервер все может ;) но лимит есть лимит. У вас есть другие варианты, как обходить ограничение в 30 подписок, если не убивать их при закрытии окон? Поделитесь :) мультиплексирование всех событий через одну подписку в одной вкладке и распространение этого в другие вкладки не предлагать, уже проходили. Каждая вкладка должна иметь свой личный канал для нотификаций.
                                                                        • 0
                                                                          Т.е. если я открою 30 вкладок, потом убью процесс (завис компьютер). Через минуту открою его, то ваше приложение тупо не будет работать?
                                                                          Поделитесь :)

                                                                          Делюсь. Не делать подписку на вкладки, делать на реальные объекты: пользователя, страницу (ту, которая по URL), модули. Например, подписка для пользователя на этот блок сообщений. Тогда серверу не нужно готовить одно и то же бесконечное (в теории) число раз.
                                                                          Однако, вы не пояснили в чем логика этого лимита (в 30), возможно, это ограничение заложено в бизнес-процессе? Или это просто этакое желание сэкономить программистов за счет потери клиентов?
                                                                          • –1
                                                                            Лимит не я контролирую, это данность и она не изменится. Понятия не имею, в чем логика.

                                                                            «делать на реальные объекты: пользователя, страницу (ту, которая по URL), модули» — это общие слова, напрочь лишенные смысла. На какие, по-вашему, объекты распространяется подписка, о которой говорю я?

                                                                            Сервер и так не готовит ничего бесконечное кол-во раз. Нотификации рассылаются отдельной подсистемой, которая при приходе события поднимает живые подписки и в них пишет данные. В одной подписке может быть несколько совершенно разных событий задействовано и каждая вкладка подписывается только на нужные ей лично.

                                                                            Про Ваш юз-кейс — вы часто открывали 30 окон одного сайта? И нет, ничего не сломается, подписки будут убиваться в неактивных вкладках и создаваться в активных.

                                                                            У Вас есть опыт создания подобных приложений? Игр, чатов, мессенджеров? Серверных частей для этого? Или Вы просто умозрительно рассказываете?
                                                                            • 0
                                                                              На какие, по-вашему, объекты распространяется подписка, о которой говорю я?

                                                                              Вы говорите «на вкладки», у каждой вкладки своя персональная подписка на сервере.
                                                                              Лимит не я контролирую, это данность и она не изменится. Понятия не имею, в чем логика.

                                                                              Впрочем, если вы не контролируете сервер, то о чем вообще говорить) Я рассуждала с точки зрения нормальной системы, а не когда одна часть — темная кривая лошадка, а другой приходится приспосабливаться при помощи костылей. Однако вопрос про 30 вкладок (или любой другой случай закрытия вкладки без beforeunload), так и остался без ответа.
                                                                              Про Ваш юз-кейс — вы часто открывали 30 окон одного сайта?

                                                                              Каждый день, и не 1 сайт, тот же хабр более чем часто. Вконтакте, новостные сайты, почту, да можно сказать большинство часто используемых.
                                                                              И нет, ничего не сломается, подписки будут убиваться в неактивных вкладках и создаваться в активных.

                                                                              Через 5 минут?

                                                                              У Вас есть опыт создания подобных приложений? Игр, чатов, мессенджеров? Серверных частей для этого? Или Вы просто умозрительно рассказываете?

                                                                              Когда кончаются аргументы?
                                                                              • 0
                                                                                если вы не контролируете сервер, то о чем вообще говорить

                                                                                Вот именно.
                                                                                • 0
                                                                                  Вот именно.

                                                                                  Просто признайте: либо проблема неработающих вкладок и «небольших» (на самом деле очень даже больших) 200мс существует, либо никакие «отписки» делать не нужно.
                                                                                  • –1
                                                                                    *рука-лицо* что признавать то?

                                                                                    Код на продакшене уже год как работает, в самом начале была проблема с подписками, и эту проблему решили (выше написано как), проблема ушла, саппорт это подтверждает. Клиенты довольны, деньги идут, никто не жалуется.
                                                                                    • +1
                                                                                      То, что конкретно данный костыль никак не относится к теме синхронных запросов, и тем более, к их необходимости в браузерах.

                                                                                      А в вашем случае, можете отнести наличие beforeunload и возможность выполнения в них синхронных запросов к удаче, не факт, что завтра браузеры такую возможность не уберут (как убрали все, что мешает или опасно пользователю).
                                                                                      • –3
                                                                                        Если бы вы хоть немного знали предмет, о котором вы рассуждаете, вы бы знали, что внутри обработчика beforeunload можно выполнить некие действия, чтоб воспрепятствовать уходу пользователя со страницы (например, при наличии несохраненных данных). Действия, выполняемые в рамках обработчика *обязаны* быть синхронными, иначе он отработает и страница благополучно будет выгружена, а все незакрытые соединения принудительно разорваны.
                                                                                        • –1
                                                                                          А главное — оно так уже лет 10 работает и вряд ли когда-то поменяется.
                                                                        • +3
                                                                          Вы это, socket.io не пробовали?
                                                                          • +1
                                                                            Мне кажется, это слишком очевидно, чтобы быть решением :)
                                                                            • 0
                                                                              Ответил ниже, это и правда слишком очевидно… Но когда нет контроля над сервером — это не решение.
                                                                  • +3
                                                                    Не проще ли использовать веб-сокеты? По-идее, браузер сам закроет соединения, которые более недоступны.
                                                                    • 0
                                                                      Безусловно проще. Но их поддержки у сервера нет. И сделать с этим я ничего не могу.
                                                                      • –3
                                                                        Есть такой сервис pubnub.com, так вот у них все реализовано через лонг-поллинг, они даже веб-сокетную эмуляцию через это написали. Их аргумент следующий, якобы «Protocols, like WebSockets, can get tripped up by cell tower switching, double NAT environments, and even some anti-virus software or proxy boarder authorities.» (источник stackoverflow.com/a/26306148). Это просто к слову.
                                                              • +5
                                                                Неправда. Синхронный запрос нужен, чтобы «вешать» пользовательский интерфейс.
                                                                • –3
                                                                  Ну можно, конечно, и так использовать. Но я склонен думать, что это лишь необходимое условие синхронности.
                                                              • +1
                                                                Возможно, мой пример, будет некорректным, и я был бы рад, если бы вы подсказали, как того же самого можно добиться, без синхронного запроса, но на момент реализации, я ответа не нашёл :( Итак:

                                                                1. Пользователь в форме оплаты кликает на кнопку «оплатить»
                                                                2. Выполняется синхронный запрос к магазину, который регистрирует заказ и формирует все необходимые данные для перехода к платёжному шлюзу
                                                                3. Браузер выполняет submit POST-формы к платёжному шлюзу, со всеми необходимыми данными
                                                                4. Пользователь попадает на страницу платёжного шлюза и оплачивает заказ

                                                                Суть в том, что если в п.2 сделать асинхронный запрос, то выполнить п.3 будет уже нельзя. Браузер не позволит. А если залочить страницу на всё время п.2, то контекст обработки клика мышью по кнопке сохранится и никаких проблем с безопасностью не возникнет. А пользователю удобно, ровно 1 клик и никакой суеты. Однако, возможна ситуация, когда ответ с сервера затянется или вовсе не придёт. И тогда вкладка по сути заблокируется. Поэтому мне сильно не нравится это решение.

                                                                Был бы рад услышать, как этого можно избежать (без превентивного оформления заказа на всякий случай и без доп. сложностей для пользователя).
                                                                • +1
                                                                  Поддерживаю, была ситуация, когда нужно было открытие попапа по клику с подтверждением на сервере. Работало только синхронным запросом, увы.
                                                                  • +5
                                                                    Вы чего-то не договариваете. Первый раз слышу, чтобы браузер запрещал отправку post формы.

                                                                    По всей видимости у вас проблема в открытии нового окна через window.open или target="_blank".
                                                                    Но в этом случае ничто не мешает открыть его сразу же, и потом уже отправлять аяксы, показав при этом юзеру удобный прелоадер в попапе.
                                                                    • 0
                                                                      Похоже, что вы правы. Я попробовал сейчас повторить описанное, и POST запрос в этом же окне без проблем выполнился. И да, действительно, я упустил тот момент (попросту забыл, давно было), что задача была не закрывать вкладку с корзиной, т.е. открыв платёжный шлюз в новой вкладке. Для этого я как раз использовал "_blank". И где-то там у меня и возникли проблемы.

                                                                      По поводу открытия вкладки сразу, ещё до регистрации заказа, — хорошая идея. Должна сработать. Спасибо. Учту (теперь уже на будущее). Кажется, я что-то подобное пытался провернуть, но у меня не получилось. А в чём была загвоздка уже не помню :( Но скорее всего должно сработать без нареканий. Ведь достаточно того, что браузер позволяет сделать POST-«переход» без каких-либо ограничений.
                                                                  • 0
                                                                    Тесты гораздо удобней писать в синхронном стиле, чем в асинхронном.
                                                                    Динамическая каскадная подгрузка зависимостей во время разработки.
                                                                    • 0
                                                                      Тесты гораздо удобней писать в синхронном стиле, чем в асинхронном.

                                                                      В тестах http-вызовы обычно мокаются? Вроде речь не шла о том, чтобы все операции сделать асинхронными? Типа вместо оператора "+", сделать асинхронную функцию sum(6,7)?
                                                                  • 0
                                                                    Хорошие вопросы! Увы, автор оригинального поста не затрагивает их. Но, немного порывшись в спеках, можно без труда на них ответить:

                                                                    — Могу ли я отправить/получить файл?
                                                                    Да, без проблем

                                                                    — Могу ли я следить за прогрессом выполнения запроса?
                                                                    Проще говоря, нет. Будет только состояние Promise'a, при выполнении которого (Promise'а) у вас будет Stream-объект с финальным статусом. Соответственно, на данный момент так же невозможно отследить % загрузки файла.

                                                                    — Могу ли я сделать синхронный запрос?
                                                                    Нет, и не факт, что это может быть доступно в дальнейшем. Но, лично я не вижу профита в этой опции. Если в данный момент можно установить XHR флаг «выполнись синхронно», то ничего не мешает сделать это асинхронно и вызывать др. функции в цепочке Promise'ов, как это сделано в примере с цепочками Promise'ов.
                                                                  • –1
                                                                    Спасибо за перевод. То, что возвращаеться stream-объект — это круто, теперь еще один пласт API отпадает для FRP библиотек, которые поддерживают промисы.
                                                                    • 0
                                                                      И главный вопрос, при наличии проблем с сетью XMLHttpRequest не вызывает callback onerror, и соответственно нет возможности никак обработать ошибку, точнее на такую ошибку не вызывается вообще никакой обработчик. Что нибудь изменилось в этом плане в Fetch API?
                                                                      • 0
                                                                        Разве спустя определенное время, запрос не отваливается по таймауту?
                                                                        • 0
                                                                          отваливается, если его установить, но я не могу его установить, так как его нужно будет ставить довольно большой, и смысл в нем теряется. А если поставить маленький, то это сломает те запросы которые не могут быть выполнены быстро.
                                                                          Мне нужно явно получить, что у меня есть сетевая ошибка, чтобы в таком случае можно было бы например повторить запрос, ну и как минимум собрать информацию о таких ошибках, сейчас я не могу собрать это, только по сообщениям пользователей.
                                                                          • 0
                                                                            О каких «проблем с сетью» и «сетевых ошибках» идет речь?
                                                                            • 0
                                                                              например ошибка в Chrome «net::ERR_CONNECTION_TIMED_OUT» и другие похожие ошибки
                                                                              • 0
                                                                                Ну так для них onerror вполне себе вызывается.
                                                                                • 0
                                                                                  Если бы вызывался не было бы вопросов. Но нет, он не срабатывает.
                                                                      • 0
                                                                        А default options можно ли как нибудь запомнить (например всегда передавать заголовок для аутентификации) и использовать для всех последующих запросов?
                                                                        • 0
                                                                          Да, в ближайшем будующем это будет возможно. Пока можно посмотреть как это будет работать на MDN
                                                                          • 0
                                                                            В полифиле уже есть реализация Headers
                                                                            • 0
                                                                              В общем то всех интерфейсов включая также Response, Request и Body
                                                                        • +3
                                                                          Гигантский шаг вперед конечно, но одного меня смущает название? Да в большинстве случаев метод наверняка будет использоваться для подтягивания чего-либо GET запросом, но в случае POST название «fetch()» является ложным.
                                                                          • 0
                                                                            Согласен, название какое-то неправильное
                                                                            • 0
                                                                              Не вас одного. Называли бы request, например
                                                                              • 0
                                                                                request — суть тоже самое, что-то запросить/получить. А универсальным должно быть что-то, означающее равноценный обмен информацией (data exchange?).
                                                                                • +1
                                                                                  Запрос на изменение данных — вполне себе реквест.
                                                                                  • 0
                                                                                    Вы правы, так логичнее. В таком случае fetch еще ближе к истине, ведь слово «запрос» не говорит о том, что функция вернет.
                                                                                    fetch — «получение ответа на запрос об изменении данных».
                                                                                    • 0
                                                                                      А мне не надо получить ответ на запрос об изменении данных. Мне надо их изменить.
                                                                                      • 0
                                                                                        Все верно, но функция выполняет и то и то, мы же говорим о ее названии? А вы, видимо, хотите другую функцию — которая не возвращает результат изменения данных?
                                                                            • 0
                                                                              Thank you
                                                                              • 0
                                                                                «Скоуп» — это, надо полагать, область видимости?
                                                                              • +1
                                                                                Данный код из примера автора, можно упростить:
                                                                                function status(response) {  
                                                                                  if (response.status >= 200 && response.status < 300) {  
                                                                                    return Promise.resolve(response)  
                                                                                  } else {  
                                                                                    return Promise.reject(new Error(response.statusText))  
                                                                                  }  
                                                                                }
                                                                                

                                                                                на:
                                                                                function status(response) {  
                                                                                  // свойство ok = true, когда status в диапазоне 200-299
                                                                                  if (response.ok) {  
                                                                                    // не надо лишний раз оборачивать Promise в Promise
                                                                                    return response;  
                                                                                  } else {
                                                                                    // здесь можно просто кинуть exception
                                                                                    throw new Error(response.statusText);
                                                                                  }  
                                                                                }
                                                                                
                                                                                • 0
                                                                                  Если Вы уберете Promises, не получится использовать цепочки вызовов, как указано в примере. Но в целом да, можно и так.
                                                                                  • 0
                                                                                    Никто убрать обещания не предлагает. Речь идет о вот этом куске кода:

                                                                                    function status(responce) {
                                                                                      // ...
                                                                                    }
                                                                                    
                                                                                    fetch('users.json')  
                                                                                      .then(status)
                                                                                       //...
                                                                                    


                                                                                    То есть функция status — это один из элементов цепочки. VBauer говорит лишь о том, что внутри функции, которая передается в then, не обязательно возвращать обещание — любое «обычное» значение будет автоматически обернуто в resolve, а любое исключение будет автоматически обернуто в reject.
                                                                                    • 0
                                                                                      Точно!
                                                                                      • 0
                                                                                        Честно говоря, не знал про автоматическую обёртку в Promise. В таком случае Вы правы и пример действительно можно упростить.
                                                                                • +2

                                                                                  Читаю и не понимаю в каком мире живут разработчики стандарта...


                                                                                  Невозможно отменить и невозможно получить прогресс — все, можно сразу закапывать, потому что в этих случаях придется использовать XHR и тогда лучше использовать только его. Едиственное достоинство это стримы, но они нужны далеко не всем.

                                                                                  Только полноправные пользователи могут оставлять комментарии. Войдите, пожалуйста.