1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
|
---
title: Получение данных с сервера
slug: Learn/JavaScript/Client-side_web_APIs/Fetching_data
tags:
- AJAX
- API
- Fetch
- JavaScript
- XHR
- Новичку
translation_of: Learn/JavaScript/Client-side_web_APIs/Fetching_data
---
<div>{{LearnSidebar}}</div>
<div>{{PreviousMenuNext("Learn/JavaScript/Client-side_web_APIs/Manipulating_documents", "Learn/JavaScript/Client-side_web_APIs/Third_party_APIs", "Learn/JavaScript/Client-side_web_APIs")}}</div>
<p class="summary"><span lang="ru">Другой очень распространенной задачей в современных веб-сайтах и приложениях является получение отдельных элементов данных с сервера для обновления разделов веб-страницы без необходимости загрузки всей новой страницы. Эта, казалось бы, небольшая деталь оказала огромное влияние на производительность и поведение сайтов, поэтому в этой статье мы объясним концепцию и рассмотрим технологии, которые делают это возможным, например XMLHttpRequest и API Fetch.</span></p>
<table class="learn-box standard-table">
<tbody>
<tr>
<th scope="row">Необходимые условия:</th>
<td>Основы JavaScript (см. <a href="/en-US/docs/Learn/JavaScript/First_steps">первые шаги</a>, <a href="/en-US/docs/Learn/JavaScript/Building_blocks">структурные элементы</a>, <a href="/en-US/docs/Learn/JavaScript/Objects">объекты JavaScript</a>), <a href="/en-US/docs/Learn/JavaScript/Client-side_web_APIs/Introduction">основы клиентских API</a></td>
</tr>
<tr>
<th scope="row">Задача:</th>
<td>Узнать, как извлекать данные с сервера и использовать их для обновления содержимого веб-страницы.</td>
</tr>
</tbody>
</table>
<h2 id="В_чем_проблема">В чем проблема?</h2>
<p>Первоначальная загрузка страницы в Интернете была простой - вы отправляли запрос на сервер web-сайта, и если всё работает, как и должно, то вся необходимая информация о странице будет загружена и отображена на вашем компьютере.</p>
<p><img alt="A basic representation of a web site architecture" src="https://mdn.mozillademos.org/files/6475/web-site-architechture@2x.png" style="display: block; height: 134px; margin: 0px auto; width: 484px;"></p>
<p>Проблема с этой моделью заключается в том, что всякий раз, когда вы хотите обновить любую часть страницы, например, чтобы отобразить новый набор продуктов или загрузить новую страницу, вам нужно снова загрузить всю страницу. Это очень расточительно и приводит к плохому пользовательскому опыту, особенно по мере того, как страницы становятся все более сложными.</p>
<h3 id="Появление_Ajax">Появление Ajax</h3>
<p>Это привело к созданию технологий, позволяющих веб-страницам запрашивать небольшие фрагменты данных (например, <a href="https://developer.mozilla.org/en-US/docs/Web/HTML">HTML</a>, {{glossary("XML")}}, <a href="https://developer.mozilla.org/en-US/docs/Learn/JavaScript/Objects/JSON">JSON</a> или обычный текст) и отображать их только при необходимости, помогая решать проблему, описанную выше.</p>
<p>Это достигается с помощью таких API, как {{domxref("XMLHttpRequest")}} или - более новой - <a href="https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API">Fetch API</a>. Эти технологии позволяют веб-страницам напрямую обрабатывать запросы <a href="https://developer.mozilla.org/en-US/docs/Web/HTTP">HTTP</a> для определенных ресурсов, доступных на сервере, и форматировать результирующие данные по мере необходимости перед их отображением.</p>
<div class="note">
<p><strong>Примечание</strong>: Вначале эта общая техника была известна как Асинхронный JavaScript и XML (Ajax), поскольку она, как правило, использовала {{domxref("XMLHttpRequest")}} для запроса данных XML. В наши дни это обычно не так (вы, скорее всего, будете использовать <code>XMLHttpRequest</code> или Fetch для запроса JSON), но результат все тот же, и термин «Ajax» по-прежнему часто используется для описания этой техники.</p>
</div>
<p><img alt="A simple modern architecture for web sites" src="https://mdn.mozillademos.org/files/6477/moderne-web-site-architechture@2x.png" style="display: block; height: 235px; margin: 0px auto; width: 559px;"></p>
<p>Модель Ajax предполагает использование веб-API в качестве прокси для более разумного запроса данных, а не просто для того, чтобы браузер перезагружал всю страницу. Давайте подумаем о значении этого:</p>
<ol>
<li>Перейдите на один из ваших любимых сайтов, богатых информацией, таких как Amazon, YouTube, CNN и т.д., и загрузите его.</li>
<li>Теперь найдите что-нибудь, например, новый продукт. Основной контент изменится, но большая часть информации, подобной заголовку, нижнему колонтитулу, навигационному меню и т. д., останется неизменной.</li>
</ol>
<p>Это действительно хорошо, потому что:</p>
<ul>
<li>Обновления страницы намного быстрее, и вам не нужно ждать перезагрузки страницы, а это означает, что сайт работает быстрее и воспринимается более отзывчивым.</li>
<li>Меньше данных загружается при каждом обновлении, что означает меньшее потребление пропускной способности. Это не может быть такой большой проблемой на рабочем столе в широкополосном подключении, но это серьезная проблема на мобильных устройствах и в развивающихся странах, которые не имеют повсеместного быстрого интернет-сервиса.</li>
</ul>
<p>Чтобы ускорить работу, некоторые сайты также сохраняют необходимые файлы и данные на компьютере пользователя при первом обращении к сайту, а это означает, что при последующих посещениях они используют локальные версии вместо загрузки свежих копий, как при первой загрузке страницы. Содержимое загружается с сервера только при его обновлении.</p>
<p><img alt="A basic web app data flow architecture" src="https://mdn.mozillademos.org/files/6479/web-app-architecture@2x.png" style="display: block; height: 383px; margin: 0px auto; width: 562px;"></p>
<h2 id="Основной_запрос_Ajax">Основной запрос Ajax</h2>
<p>Давайте посмотрим, как обрабатывается такой запрос, используя как {{domxref ("XMLHttpRequest")}}, так и <a href="https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API">Fetch</a>. В этих примерах мы будем запрашивать данные из нескольких текстовых файлов и использовать их для заполнения области содержимого.</p>
<p>Этот набор файлов будет действовать как наша поддельная база данных; в реальном приложении мы с большей вероятностью будем использовать серверный язык, такой как PHP, Python или Node, чтобы запрашивать наши данные из базы данных. Здесь, однако, мы хотим сохранить его простым и сосредоточиться на стороне клиента.</p>
<h3 id="XMLHttpRequest">XMLHttpRequest</h3>
<p><code>XMLHttpRequest</code> (который часто сокращается до XHR) является довольно старой технологией сейчас - он был изобретен Microsoft в конце 1990-х годов и уже довольно долго стандартизирован в браузерах.</p>
<ol>
<li>
<p>Чтобы начать этот пример, создайте локальную копию <a href="https://github.com/mdn/learning-area/blob/master/javascript/apis/fetching-data/ajax-start.html">ajax-start.html</a> и четырех текстовых файлов - <a href="https://github.com/mdn/learning-area/blob/master/javascript/apis/fetching-data/verse1.txt">verse1.txt</a>, <a href="https://github.com/mdn/learning-area/blob/master/javascript/apis/fetching-data/verse2.txt">verse2.txt</a>, <a href="https://github.com/mdn/learning-area/blob/master/javascript/apis/fetching-data/verse3.txt">verse3.txt</a> и <a href="https://github.com/mdn/learning-area/blob/master/javascript/apis/fetching-data/verse4.txt">verse4.txt</a> - в новом каталоге на вашем компьютере. В этом примере мы загрузим другое стихотворение (который вы вполне можете распознать) через XHR, когда он будет выбран в выпадающем меню.</p>
</li>
<li>
<p>Внутри элемента {{htmlelement("script")}} добавьте следующий код. В нем хранится ссылка на элементы {{htmlelement("select")}} и {{htmlelement("pre")}} в переменных и определяется {{domxref ("GlobalEventHandlers.onchange", "onchange")}} обработчика событий, так что, когда значение select изменяется, его значение передается вызываемой функции <code>updateDisplay()</code> в качестве параметра.</p>
<pre class="brush: js">var verseChoose = document.querySelector('select');
var poemDisplay = document.querySelector('pre');
verseChoose.onchange = function() {
var verse = verseChoose.value;
updateDisplay(verse);
};</pre>
</li>
<li>
<p>Давайте определим нашу функцию <code>updateDisplay()</code>. Прежде всего, поставьте следующее ниже своего предыдущего блока кода - это пустая оболочка функции:</p>
<pre class="brush: js">function updateDisplay(verse) {
};</pre>
</li>
<li>
<p>Мы начнем нашу функцию с создания относительного URL-адреса, указывающего на текстовый файл, который мы хотим загрузить и который понадобится нам позже. Значение элемента {{htmlelement("select")}} в любой момент совпадает с текстом внутри выбранного {{htmlelement("option")}} (если вы не укажете другое значение в атрибуте value) - например, «Verse 1». Соответствующий текстовый файл стиха является «verse1.txt» и находится в том же каталоге, что и файл HTML, поэтому будет использоваться только имя файла.</p>
<p>Тем не менее, веб-серверы, как правило, чувствительны к регистру, и имя файла не имеет символа "пробела". Чтобы преобразовать «Verse 1» в «verse1.txt», нам нужно преобразовать V в нижний регистр, удалить пробел и добавить .txt в конец. Это можно сделать с помощью {{jsxref("String.replace", "replace ()")}}, {{jsxref("String.toLowerCase", "toLowerCase ()")}} и простой <a href="https://developer.mozilla.org/en-US/docs/Learn/JavaScript/First_steps/Strings#Concatenating_strings">конкатенации строк</a>. Добавьте следующие строки внутри функции <code>updateDisplay()</code>:</p>
<pre class="brush: js">verse = verse.replace(" ", "");
verse = verse.toLowerCase();
var url = verse + '.txt';</pre>
</li>
<li>
<p>Чтобы начать создание запроса XHR, вам нужно создать новый объект запроса, используя конструктор {{domxref("XMLHttpRequest()")}}. Вы можете назвать этот объект так, как вам нравится, но мы будем называть его <code>request</code> (запросом), чтобы все было просто. Добавьте следующие ниже строки:</p>
<pre class="brush: js">var request = new XMLHttpRequest();</pre>
</li>
<li>
<p>Затем вам нужно использовать метод {{domxref("XMLHttpRequest.open", "open()")}}, чтобы указать, какой <a href="https://developer.mozilla.org/en-US/docs/Web/HTTP/Methods">HTTP request method</a> использовать для запроса ресурса из сети и какой его URL-адрес. Мы просто используем метод <code><a href="https://developer.mozilla.org/en-US/docs/Web/HTTP/Methods/GET">GET</a></code> здесь и задаем URL как нашу переменную <code>url</code>. Добавьте это ниже вашей предыдущей строки:</p>
<pre class="brush: js">request.open('GET', url);</pre>
</li>
<li>
<p>Затем мы зададим тип ожидаемого ответа, который определяется как свойство {{domxref("XMLHttpRequest.responseType", "responseType")}} - как <code>text</code>. Здесь это не является абсолютно необходимым - XHR возвращает текст по умолчанию - но это хорошая идея, чтобы привыкнуть к настройке этого, если вы хотите получить другие типы данных в будущем. Добавьте следующее:</p>
<pre class="brush: js">request.responseType = 'text';</pre>
</li>
<li>
<p>Получение ресурса из сети - это {{glossary("asynchronous")}} операция, означающая, что вам нужно дождаться завершения этой операции (например, ресурс возвращается из сети), прежде чем вы сможете сделать что-либо с этим ответом, иначе будет выброшена ошибка. XHR позволяет вам обрабатывать это, используя обработчик события {{domxref("XMLHttpRequest.onload", "onload")}} - он запускается при возникновении события {{event("load")}} (когда ответ вернулся). Когда это произойдет, данные ответа будут доступны в свойстве <code>response</code> (ответ) объекта запроса XHR.</p>
<p>Добавьте следующее ниже вашего последнего дополнения. Вы увидите, что внутри обработчика события <code>onload</code> мы устанавливаем textContent <code>poemDisplay</code> (элемент {{htmlelement("pre")}}) в значение {{domxref("XMLHttpRequest.response", "request. response ")}}.</p>
<pre class="brush: js">request.onload = function() {
poemDisplay.textContent = request.response;
};</pre>
</li>
<li>
<p>Вышеприведенная конфигурация запроса XHR фактически не будет выполняться до тех пор, пока мы не вызовем метод {{domxref("XMLHttpRequest.send", "send()")}}. Добавьте следующее ниже вашего предыдущего дополнения для вызова функции:</p>
<pre class="brush: js">request.send();</pre>
</li>
<li>
<p>Одна из проблем с примером заключается в том, что он не покажет ни одного стихотворения, когда он впервые загружается. Чтобы исправить это, добавьте следующие две строки внизу вашего кода (чуть выше закрывающего тега <code></script></code>), чтобы загрузить стих 1 по умолчанию и убедитесь, что элемент {{htmlelement("select")}} всегда показывает правильное значение:</p>
<pre class="brush: js">updateDisplay('Verse 1');
verseChoose.value = 'Verse 1';</pre>
</li>
</ol>
<h3 id="Обслуживание_вашего_примера_с_сервера">Обслуживание вашего примера с сервера</h3>
<p>Некоторые браузеры (включая Chrome) не будут запускать запросы XHR, если вы просто запускаете пример из локального файла. Это связано с ограничениями безопасности (для получения дополнительной информации о безопасности в Интернете, ознакомьтесь с <a href="/en-US/docs/Learn/Server-side/First_steps/Website_security">Website security</a>).</p>
<p>Чтобы обойти это, нам нужно протестировать пример, запустив его через локальный веб-сервер. Чтобы узнать, как это сделать, прочитайте <a href="/en-US/docs/Learn/Common_questions/set_up_a_local_testing_server">Как настроить локальный тестовый сервер?</a></p>
<h3 id="Fetch">Fetch</h3>
<p>API-интерфейс Fetch - это, в основном, современная замена XHR - недавно он был представлен в браузерах для упрощения асинхронных HTTP-запросов в JavaScript, как для разработчиков, так и для других API, которые строятся поверх Fetch.</p>
<p>Давайте преобразуем последний пример, чтобы использовать Fetch!</p>
<ol>
<li>
<p>Сделайте копию своего предыдущего готового каталога примеров. (Если вы не работали над предыдущим упражнением, создайте новый каталог и внутри него создайте копии <a href="https://github.com/mdn/learning-area/blob/master/javascript/apis/fetching-data/xhr-basic.html">xhr-basic.html</a> и четырех текстовых файлов — <a href="https://github.com/mdn/learning-area/blob/master/javascript/apis/fetching-data/verse1.txt">verse1.txt</a>, <a href="https://github.com/mdn/learning-area/blob/master/javascript/apis/fetching-data/verse2.txt">verse2.txt</a>, <a href="https://github.com/mdn/learning-area/blob/master/javascript/apis/fetching-data/verse3.txt">verse3.txt</a> и <a href="https://github.com/mdn/learning-area/blob/master/javascript/apis/fetching-data/verse4.txt">verse4.txt</a>.)</p>
</li>
<li>
<p>Внутри функции <code>updateDisplay()</code> найдите код XHR:</p>
<pre class="brush: js">var request = new XMLHttpRequest();
request.open('GET', url);
request.responseType = 'text';
request.onload = function() {
poemDisplay.textContent = request.response;
};
request.send();</pre>
</li>
<li>
<p>Замените весь XHR-код следующим:</p>
<pre class="brush: js">fetch(url).then(function(response) {
response.text().then(function(text) {
poemDisplay.textContent = text;
});
});</pre>
</li>
<li>
<p>Загрузите пример в свой браузер (запустите его через веб-сервер), и он должен работать так же, как и версия XHR, при условии, что вы используете современный браузер.</p>
</li>
</ol>
<h4 id="Итак_что_происходит_в_коде_Fetch">Итак, что происходит в коде Fetch?</h4>
<p>Прежде всего, мы вызываем метод {{domxref("WorkerOrWindowGlobalScope.fetch()", "fetch()")}}, передавая ему URL-адрес ресурса, который мы хотим получить. Это современный эквивалент {{domxref("XMLHttpRequest.open", "request.open()")}} в XHR, плюс вам не нужен эквивалент <code>.send()</code>.</p>
<p>После этого вы можете увидеть метод {{jsxref("Promise.then", ".then()")}}, прикреплённый в конец <code>fetch()</code> - этот метод является частью {{jsxref("Promise","Promises")}} - современная функция JavaScript для выполнения асинхронных операций. <code>fetch()</code> возвращает обещание, которое разрешает ответ, отправленный обратно с сервера, - мы используем <code>.then()</code> для запуска некоторого последующего кода после того, как обещание будет разрешено, что является функцией, которую мы определили внутри нее. Это эквивалент обработчика события <code>onload</code> в XHR-версии.</p>
<p>Эта функция автоматически передает ответ от сервера в качестве параметра, когда обещает <code>fetch()</code>. Внутри функции мы берем ответ и запускаем его метод {{domxref("Body.text", "text()")}}, который в основном возвращает ответ как необработанный текст. Это эквивалент <code>request.responseType = 'text'</code> в версии XHR.</p>
<p>Вы увидите, что <code>text()</code>также возвращает обещание, поэтому мы привязываем к нему другой <code>.then()</code>, внутри которого мы определяем функцию для получения необработанного текста, который обещает решение <code>text()</code>.</p>
<p>Внутри функции внутреннего обещания мы делаем то же самое, что и в версии XHR, - устанавливаем текстовое содержимое {{htmlelement("pre")}} в текстовое значение.</p>
<h3 id="Помимо_обещаний">Помимо обещаний</h3>
<p>Обещания немного запутывают первый раз, когда вы их встречаете, но не беспокойтесь об этом слишком долго. Через некоторое время вы привыкнете к ним, особенно, когда вы узнаете больше о современных JavaScript-API. Большинство из них в большей степени основаны на обещаниях.</p>
<p>Давайте посмотрим на структуру обещаний сверху, чтобы увидеть, можем ли мы еще немного понять это:</p>
<pre class="brush: js">fetch(url).then(function(response) {
response.text().then(function(text) {
poemDisplay.textContent = text;
});
});</pre>
<p>В первой строке говорится: «Получить ресурс, расположенный по адресу url» <code>(fetch(url)</code>) и «затем запустить указанную функцию, когда обещание будет разрешено» (<code>.then(function() { ... })</code>). «Resolve» означает «завершить выполнение указанной операции в какой-то момент в будущем». Указанная операция в этом случае заключается в извлечении ресурса с указанного URL (с использованием HTTP-запроса) и возврата ответа для нас, чтобы что-то сделать.</p>
<p>Фактически, функция, переданная в <code>then()</code>, представляет собой кусок кода, который не запускается немедленно - вместо этого он будет работать в какой-то момент в будущем, когда ответ будет возвращен. Обратите внимание, что вы также можете сохранить свое обещание в переменной и цепочку {{jsxref("Promise.then", ".then()")}} вместо этого. Ниже код будет делать то же самое:</p>
<pre class="brush: js">var myFetch = fetch(url);
myFetch.then(function(response) {
response.text().then(function(text) {
poemDisplay.textContent = text;
});
});</pre>
<p>Поскольку метод <code>fetch()</code> возвращает обещание, которое разрешает HTTP-ответ, любая функция, которую вы определяете внутри <code>.then()</code>, прикованная к концу, будет автоматически передаваться как параметр. Вы можете вызвать параметр, который вам нравится - приведенный ниже пример будет работать:</p>
<pre class="brush: js">fetch(url).then(function(dogBiscuits) {
dogBiscuits.text().then(function(text) {
poemDisplay.textContent = text;
});
});</pre>
<p>Но имеет смысл называть параметр тем, что описывает его содержимое!</p>
<p>Теперь давайте сосредоточимся только на функции:</p>
<pre class="brush: js">function(response) {
response.text().then(function(text) {
poemDisplay.textContent = text;
});
}</pre>
<p>Объект ответа имеет метод {{domxref("Body.text", "text()")}}, который берет необработанные данные, содержащиеся в теле ответа, и превращает его в обычный текст, который является форматом, который мы хотим в нем А также возвращает обещание (которое разрешает полученную текстовую строку), поэтому здесь мы используем другой {{jsxref("Promise.then", ".then()")}}, внутри которого мы определяем другую функцию, которая диктует что мы хотим сделать с этой текстовой строкой. Мы просто устанавливаем свойство <code><a href="https://developer.mozilla.org/en-US/docs/Web/API/Node/textContent">textContent</a></code> элемента {{htmlelement("pre")}} нашего стихотворения равным текстовой строке, так что это получается довольно просто.</p>
<p>Также стоит отметить, что вы можете напрямую связывать несколько блоков обещаний (<code>.then()</code>, но есть и другие типы) на конце друг друга, передавая результат каждого блока следующему блоку по мере продвижения по цепочке , Это делает обещания очень мощными.</p>
<p>Следующий блок делает то же самое, что и наш оригинальный пример, но написан в другом стиле:</p>
<pre class="brush: js">fetch(url).then(function(response) {
return response.text()
}).then(function(text) {
poemDisplay.textContent = text;
});</pre>
<p>Многие разработчики любят этот стиль больше, поскольку он более плоский и, возможно, легче читать для более длинных цепочек обещаний - каждое последующее обещание приходит после предыдущего, а не внутри предыдущего (что может стать громоздким). Единственное отличие состоит в том, что мы должны были включить оператор <code><a href="https://developer.mozilla.org/en-US/docs/Learn/JavaScript/Building_blocks/Return_values">return</a></code> перед <code>response.text()</code>, чтобы заставить его передать результат в следующую ссылку в цепочке.</p>
<h3 id="Какой_механизм_следует_использовать">Какой механизм следует использовать?</h3>
<p>Это действительно зависит от того, над каким проектом вы работаете. XHR существует уже давно и имеет отличную кросс-браузерную поддержку. Fetch and Promises, с другой стороны, являются более поздним дополнением к веб-платформе, хотя они хорошо поддерживаются в браузере, за исключением Internet Explorer и Safari (которые на момент написания Fetch были доступны в своем предварительный просмотр технологии).</p>
<p>Если вам необходимо поддерживать старые браузеры, тогда может быть предпочтительным решение XHR. Если, однако, вы работаете над более прогрессивным проектом и не так обеспокоены старыми браузерами, то Fetch может быть хорошим выбором.</p>
<p>Вам действительно нужно учиться - Fetch станет более популярным, так как Internet Explorer отказывается от использования (IE больше не разрабатывается, в пользу нового браузера Microsoft Edge), но вам может понадобиться XHR еще некоторое время.</p>
<h2 id="Более_сложный_пример">Более сложный пример</h2>
<p>Чтобы завершить статью, мы рассмотрим несколько более сложный пример, который показывает более интересные применения Fetch. Мы создали образец сайта под названием The Can Store - это вымышленный супермаркет, который продает только консервы. Вы можете найти этот пример <a href="https://mdn.github.io/learning-area/javascript/apis/fetching-data/can-store/">в прямом эфире на GitHub</a> и <a href="https://github.com/mdn/learning-area/tree/master/javascript/apis/fetching-data/can-store">посмотреть исходный код</a>.</p>
<p><img alt="A fake ecommerce site showing search options in the left hand column, and product search results in the right hand column." src="https://mdn.mozillademos.org/files/14779/can-store.png" style="display: block; margin: 0 auto;"></p>
<p>По умолчанию на сайте отображаются все продукты, но вы можете использовать элементы управления формы в столбце слева, чтобы отфильтровать их по категориям, поисковому запросу или и тому и другому.</p>
<p>Существует довольно много сложного кода, который включает фильтрацию продуктов по категориям и поисковым запросам, манипулирование строками, чтобы данные отображались правильно в пользовательском интерфейсе и т.д. Мы не будем обсуждать все это в статье, но вы можете найти обширные комментарии в коде (см. <a href="https://github.com/mdn/learning-area/blob/master/javascript/apis/fetching-data/can-store/can-script.js">can-script.js</a>).</p>
<p>Однако мы объясним код Fetch.</p>
<p>Первый блок, который использует Fetch, можно найти в начале JavaScript:</p>
<pre class="brush: js">fetch('products.json').then(function(response) {
if(response.ok) {
response.json().then(function(json) {
products = json;
initialize();
});
} else {
console.log('Network request for products.json failed with response ' + response.status + ': ' + response.statusText);
}
});</pre>
<p>Это похоже на то, что мы видели раньше, за исключением того, что второе обещание находится в условном выражении. В этом случае мы проверяем, был ли возвращенный ответ успешным - свойство {{domxref("response.ok")}} содержит логическое значение, которое <code>true</code>, если ответ был в порядке (например, <a href="https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/200">200 meaning "OK"</a>) или <code>false</code>, если он не увенчался успехом.</p>
<p>Если ответ был успешным, мы выполняем второе обещание - на этот раз мы используем {{domxref("Body.json", "json()")}}, а не {{domxref("Body.text", "text()")}}, так как мы хотим вернуть наш ответ как структурированные данные JSON, а не обычный текст.</p>
<p>Если ответ не увенчался успехом, мы выводим сообщение об ошибке в консоль, в котором сообщается о сбое сетевого запроса, который сообщает о статусе сети и описательном сообщении ответа (содержащемся в {{domxref("response.status")}} и {{domxref("response.statusText")}}, соответственно). Конечно, полный веб-сайт будет обрабатывать эту ошибку более грациозно, отображая сообщение на экране пользователя и, возможно, предлагая варианты для исправления ситуации.</p>
<p>Вы можете проверить сам случай отказа:</p>
<ol>
<li>Создание локальной копии файлов примеров (загрузка и распаковка <a href="https://github.com/mdn/learning-area/blob/master/javascript/apis/fetching-data/can-store/can-store.zip?raw=true">the can-store ZIP file</a>)</li>
<li>Запустите код через веб-сервер (как описано выше, в {{anch("Serving your example from a server")}})</li>
<li>Измените путь к извлеченному файлу, например, «product.json» (т.е. убедитесь, что он написан неправильно)</li>
<li>Теперь загрузите индексный файл в свой браузер (например, через <code>localhost:8000</code>) и посмотрите в консоли разработчика браузера. Вы увидите сообщение в строке «Запрос сети для продуктов.json не удалось с ответом 404: Файл не найден»</li>
</ol>
<p>Второй блок Fetch можно найти внутри функции <code>fetchBlob()</code>:</p>
<pre class="brush: js">fetch(url).then(function(response) {
if(response.ok) {
response.blob().then(function(blob) {
objectURL = URL.createObjectURL(blob);
showProduct(objectURL, product);
});
} else {
console.log('Network request for "' + product.name + '" image failed with response ' + response.status + ': ' + response.statusText);
}
});</pre>
<p>Это работает во многом так же, как и предыдущий, за исключением того, что вместо использования {{domxref("Body.json", "json()")}} мы используем {{domxref("Body.blob", "blob()")}} - в этом случае мы хотим вернуть наш ответ в виде файла изображения, а формат данных, который мы используем для этого - <a href="https://developer.mozilla.org/en-US/docs/Web/API/Blob">Blob</a> - этот термин является аббревиатурой от« Binary Large Object »и может в основном использоваться для представляют собой большие файловые объекты, такие как изображения или видеофайлы.</p>
<p>После того как мы успешно получили наш blob, мы создаем URL-адрес объекта, используя {{domxref("URL.createObjectURL()", "createObjectURL()")}}. Это возвращает временный внутренний URL-адрес, указывающий на объект, указанный в браузере. Они не очень читаемы, но вы можете видеть, как выглядит, открывая приложение Can Store, Ctrl-/щелкнуть правой кнопкой мыши по изображению и выбрать опцию «Просмотр изображения» (которая может немного отличаться в зависимости от того, какой браузер вы ). URL-адрес объекта будет отображаться внутри адресной строки и должен выглядеть примерно так:</p>
<pre>blob:http://localhost:7800/9b75250e-5279-e249-884f-d03eb1fd84f4</pre>
<h3 id="Вызов_XHR_версия_the_Can_Store">Вызов: XHR версия the Can Store</h3>
<p>Мы хотели бы, чтобы вы решили преобразовать версию приложения Fetch для использования XHR в качестве полезной части практики. Возьмите <a href="https://github.com/mdn/learning-area/blob/master/javascript/apis/fetching-data/can-store/can-store.zip?raw=true">копию ZIP файла</a> и попробуйте изменить JavaScript, если это необходимо.</p>
<p>Некоторые полезные советы:</p>
<ul>
<li>Вы можете найти полезный справочный материал {{domxref("XMLHttpRequest")}}.</li>
<li>Вам в основном нужно использовать тот же шаблон, что и раньше, в примере <a href="https://github.com/mdn/learning-area/blob/master/javascript/apis/fetching-data/xhr-basic.html">XHR-basic.html</a>.</li>
<li>Однако вам нужно будет добавить обработку ошибок, которые мы показали вам в версии Fetch Can Store:
<ul>
<li>Ответ найден в <code>request.response</code> после того, как событие <code>load</code> запущено, а не в обещании <code>then()</code>.</li>
<li>О наилучшем эквиваленте Fetch's <code>response.ok</code> в XHR следует проверить, является ли {{domxref("XMLHttpRequest.status","request.status")}} равным 200 или если {{domxref("XMLHttpRequest.readyState","request.readyState")}} равно 4.</li>
<li>Свойства для получения статуса и сообщения состояния одинаковы, но они находятся на объекте <code>request</code> (XHR), а не в объекте <code>response</code>.</li>
</ul>
</li>
</ul>
<div class="note">
<p><strong>Примечание</strong>: Если у вас есть проблемы с этим, не стесняйтесь сравнить свой код с готовой версией на GitHub (<a href="https://github.com/mdn/learning-area/blob/master/javascript/apis/fetching-data/can-store-xhr/can-script.js">см. исходник здесь</a>, а также <a href="https://mdn.github.io/learning-area/javascript/apis/fetching-data/can-store-xhr/">см. это в действии</a>).</p>
</div>
<h2 id="Резюме">Резюме</h2>
<p>Это завершает нашу статью по извлечению данных с сервера. К этому моменту вы должны иметь представление о том, как начать работать как с XHR, так и с Fetch.</p>
<h2 id="Смотрите_также">Смотрите также</h2>
<p>Однако в этой статье обсуждается много разных тем, которые только поцарапали поверхность. Для получения более подробной информации по этим темам, попробуйте следующие статьи:</p>
<ul>
<li><a href="/en-US/docs/AJAX/Getting_Started">Введение в Ajax</a></li>
<li><a href="/en-US/docs/Web/API/Fetch_API/Using_Fetch">Применение Fetch</a></li>
<li><a href="/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise">Promises</a></li>
<li><a href="/en-US/docs/Learn/JavaScript/Objects/JSON">Работа с JSON данными</a></li>
<li><a href="/en-US/docs/Web/HTTP/Overview">Обзор HTTP</a></li>
<li><a href="/en-US/docs/Learn/Server-side">Программирование веб-сайта на стороне сервера</a></li>
</ul>
<div>{{PreviousMenuNext("Learn/JavaScript/Client-side_web_APIs/Manipulating_documents", "Learn/JavaScript/Client-side_web_APIs/Third_party_APIs", "Learn/JavaScript/Client-side_web_APIs")}}</div>
<div>
<h2 id="В_этом_модуле">В этом модуле</h2>
<ul>
<li><a href="/en-US/docs/Learn/JavaScript/Client-side_web_APIs/Introduction">Введение в web API</a></li>
<li><a href="/en-US/docs/Learn/JavaScript/Client-side_web_APIs/Manipulating_documents">Манипулирование документами</a></li>
<li><a href="/en-US/docs/Learn/JavaScript/Client-side_web_APIs/Fetching_data">Получение данных с сервера</a></li>
<li><a href="/en-US/docs/Learn/JavaScript/Client-side_web_APIs/Third_party_APIs">Сторонние API</a></li>
<li><a href="/en-US/docs/Learn/JavaScript/Client-side_web_APIs/Drawing_graphics">Рисование графики</a></li>
<li><a href="/en-US/docs/Learn/JavaScript/Client-side_web_APIs/Video_and_audio_APIs">Видео и аудио API</a></li>
<li><a href="/en-US/docs/Learn/JavaScript/Client-side_web_APIs/Client-side_storage">Клиентское хранилище</a></li>
</ul>
</div>
|