[JavaScript, Программирование, Разработка веб-сайтов] Server-Sent Events: пример использования  
    
    
        
    
    
    
    
            
    
        
            
                
                                    
                
                                    
                
                    
                
            
        
    
    
        
            
                
                
                    
                         
                         
                       
                    
                        Автор 
                        Сообщение 
                    
                                        
                        
                            
                                
                                
                                                                                                            news_bot ®
                                                                        
                                                                                                                                                
                                                                            
                                                                                                                
                                            Стаж: 7 лет 8 месяцев                                        
                                                                                                                
                                            Сообщений: 27286                                        
                                                                                                                                                
                                                             
                            
                                
                             
                         
                        
                            
                                
                                    
                                        
                                        
 
Доброго времени суток, друзья!
В этом туториале мы рассмотрим Server Sent Events: встроенный класс EventSource, который позволяет поддерживать соединение с сервером и получать от него события.
О том, что такое SSE и для чего он используется можно почитать здесь.
Что конкретно мы будем делать?
Мы напишем простой сервер, который будет по запросу клиента отправлять ему данные 10 случайных пользователей, а клиент с помощью этих данных будет формировать карточки пользователей.
Сервер будет реализован на Node.js, клиент — на JavaScript. Для стилизации будет использоваться Bootstrap, в качестве API — Random User Generator.
Код проекта находится здесь.
Поиграть с кодом можно здесь.
Если вам это интересно, прошу следовать за мной.
Подготовка
Создаем директорию sse-tut:
mkdir sse-tut
Заходим в нее и инициализируем проект:
cd sse-tut
yarn init -y
// или
npm init -y
Устанавливаем axios:
yarn add axios
// или
npm i axios
axios будет использоваться для получения данных пользователей.
Редактируем package.json:
"main": "server.js",
"scripts": {
    "start": "node server.js"
},
Структура проекта:
sse-tut
    --node_modules
    --client.js
    --index.html
    --package.json
    --server.js
    --yarn.lock
Содержание index.html:
<head>
    <!-- Bootstrap CSS -->
    <link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.5.2/css/bootstrap.min.css" integrity="sha384-JcKb8q3iqJ61gNV9KGb8thSsNjpSL0n8PARn9HuZOnIxN0hoP+VmmDGMN5t9UJ0Z" crossorigin="anonymous">
    <style>
        .card {
            margin: 0 auto;
            max-width: 512px;
        }
        img {
            margin: 1rem;
            max-width: 320px;
        }
        p {
            margin: 1rem;
        }
    </style>
</head>
<body>
    <main class="container text-center">
        <h1>Server-Sent Events Tutorial</h1>
        <button class="btn btn-primary" data-type="start-btn">Start</button>
        <button class="btn btn-danger" data-type="stop-btn" disabled>Stop</button>
        <p data-type="event-log"></p>
    </main>
    <script src="client.js"></script>
</body>
Сервер
Приступаем к реализации сервера.
Открываем server.js.
Подключаем http и axios, определяем порт:
const http = require('http')
const axios = require('axios')
const PORT = process.env.PORT || 3000
Создаем функцию получения данных пользователя:
const getUserData = async () => {
    const response = await axios.get('https://randomuser.me/api')
    // проверяем полученные данные
    console.log(response)
    return response.data.results[0]
}
Создаем счетчик количества отправленных пользователей:
let i = 1
Пишем функцию отправки данных клиенту:
const sendUserData = (req, res) => {
    // статус ответа - 200 ок
    // соединение должно оставаться открытым
    // тип содержимого - поток событий
    // не кэшировать
    res.writeHead(200, {
        Connection: 'keep-alive',
        'Content-Type': 'text/event-stream',
        'Cache-Control': 'no-cache'
    })
    // данные будут отправляться каждые 2 секунды
    const timer = setInterval(async () => {
        // если отправлено 10 пользователей
        if (i > 10) {
            // останавливаем таймер
            clearInterval(timer)
            // сообщаем о том, что было отправлено 10 пользователей
            console.log('10 users has been sent.')
            // отправляем клиенту идентификатор со значением -1
            // для того, чтобы клиент закрыл соединение
            res.write('id: -1\ndata:\n\n')
            // закрываем соединение
            res.end()
            return
        }
        // получаем данные
        const data = await getUserData()
        // записываем данные в ответ
        // event - название события
        // id - идентификатор события; используется при повторном подключении
        // retry - время повторного подключения
        // data - данные
        res.write(`event: randomUser\nid: ${i}\nretry: 5000\ndata: ${JSON.stringify(data)}\n\n`)
        // сообщаем о том, что данные отправлены
        console.log('User data has been sent.')
        // увеличиваем значение счетчика
        i++
    }, 2000)
    // обрабатываем закрытие соединения клиентом
    req.on('close', () => {
        clearInterval(timer)
        res.end()
        console.log('Client closed the connection.')
      })
}
Создаем и запускаем сервер:
http.createServer((req, res) => {
    // обязательный заголовок для преодоления CORS
    res.setHeader('Access-Control-Allow-Origin', '*')
    // если адрес запроса - getUser
    if (req.url === '/getUsers') {
        // отправляем данные
        sendUserData(req, res)
    } else {
        // иначе, сообщаем о том, что запрашиваемая страница не найдена,
        // и закрываем соединение
        res.writeHead(404)
        res.end()
    }
}).listen(PORT, () => console.log('Server ready.'))
Полный код сервера:
SPL
const http = require('http')
const axios = require('axios')
const PORT = process.env.PORT || 3000
const getUserData = async () => {
    const response = await axios.get('https://randomuser.me/api')
    return response.data.results[0]
}
let i = 1
const sendUserData = (req, res) => {
    res.writeHead(200, {
    Connection: 'keep-alive',
    'Content-Type': 'text/event-stream',
    'Cache-Control': 'no-cache'
    })
    const timer = setInterval(async () => {
    if (i > 10) {
        clearInterval(timer)
        console.log('10 users has been sent.')
        res.write('id: -1\ndata:\n\n')
        res.end()
        return
    }
    const data = await getUserData()
    res.write(`event: randomUser\nid: ${i}\nretry: 5000\ndata: ${JSON.stringify(data)}\n\n`)
    console.log('User data has been sent.')
    i++
    }, 2000)
    req.on('close', () => {
    clearInterval(timer)
    res.end()
    console.log('Client closed the connection.')
    })
}
http.createServer((req, res) => {
    res.setHeader('Access-Control-Allow-Origin', '*')
    if (req.url === '/getUsers') {
    sendUserData(req, res)
    } else {
    res.writeHead(404)
    res.end()
    }
}).listen(PORT, () => console.log('Server ready.'))
Выполняем команду yarn start или npm start. В терминале появляется сообщение «Server ready.». Открываем http://localhost:3000:

С сервером закончили, переходим к клиентской части приложения.
Клиент
Открываем файл client.js.
Создаем функцию генерации шаблона пользовательской карточки:
const getTemplate = user => `
<div class="card">
    <div class="row">
        <div class="col-md-4">
            <img src="${user.img}" class="card-img" alt="user-photo">
        </div>
        <div class="col-md-8">
            <div class="card-body">
                <h5 class="card-title">${user.id !== null ? `Id: ${user.id}` : `User hasn't id`}</h5>
                <p class="card-text">Name: ${user.name}</p>
                <p class="card-text">Username: ${user.username}</p>
                <p class="card-text">Email: ${user.email}</p>
                <p class="card-text">Age: ${user.age}</p>
            </div>
        </div>
    </div>
</div>
`
Шаблон генерируется с использованием следующих данных: идентификатор пользователя (если имеется), имя, логин, адрес электронной почты и возраст пользователя.
Начинаем реализовывать основной функционал:
class App {
    constructor(selector) {
        // основной элемент - контейнер
        this.$ = document.querySelector(selector)
        // запускаем частный метод
        this.#init()
    }
    #init() {
        this.startBtn = this.$.querySelector('[data-type="start-btn"]')
        this.stopBtn = this.$.querySelector('[data-type="stop-btn"]')
        // контейнер для сообщений о событиях
        this.eventLog = this.$.querySelector('[data-type="event-log"]')
        // устанавливаем контекст для обработчика
        this.clickHandler = this.clickHandler.bind(this)
        // делегируем обработку события
        this.$.addEventListener('click', this.clickHandler)
    }
    clickHandler(e) {
        // если кликнули по кнопке
        if (e.target.tagName === 'BUTTON') {
            // получаем тип кнопки
            // и либо начинаем получать события от сервера,
            // либо закрываем соединение
            const {
                type
            } = e.target.dataset
            if (type === 'start-btn') {
                this.startEvents()
            } else if (type === 'stop-btn') {
                this.stopEvents()
            }
            // управление состоянием кнопок
            this.changeDisabled()
        }
    }
    changeDisabled() {
        if (this.stopBtn.disabled) {
            this.stopBtn.disabled = false
            this.startBtn.disabled = true
        } else {
            this.stopBtn.disabled = true
            this.startBtn.disabled = false
        }
    }
//...
Сначала реализуем закрытие соединения:
stopEvents() {
    this.eventSource.close()
    // сообщаем о том, что соединение закрыто пользователем
    this.eventLog.textContent = 'Event stream closed by client.'
}
Переходим к открытию потока событий:
startEvents() {
    // создаем экземпляр для получения данных по запросу на указанный адрес
    this.eventSource = new EventSource('http://localhost:3000/getUsers')
    // сообщаем о том, что соединение открыто
    this.eventLog.textContent = 'Accepting data from the server.'
    // обрабатываем получение от сервера идентификатора со значением -1
    this.eventSource.addEventListener('message', e => {
        if (e.lastEventId === '-1') {
            // закрываем соединение
            this.eventSource.close()
            // сообщаем об этом
            this.eventLog.textContent = 'End of stream from the server.'
            this.changeDisabled()
        }
        // мы можем получить такой идентификатор лишь раз
    }, {once: true})
}
Обрабатываем кастомное событие «randomUser»:
this.eventSource.addEventListener('randomUser', e => {
    // парсим полученные данные
    const userData = JSON.parse(e.data)
    // проверяем их
    console.log(userData)
    // извлекаем данные с помощью деструктуризации
    const {
        id,
        name,
        login,
        email,
        dob,
        picture
    } = userData
    // продолжаем формировать данные, необходимые для генерации пользовательской карточки
    const i = id.value
    const fullName = `${name.first} ${name.last}`
    const username = login.username
    const age = dob.age
    const img = picture.large
    const user = {
        id: i,
        name: fullName,
        username,
        email,
        age,
        img
    }
    // генерируем шаблон
    const template = getTemplate(user)
    // рендерим карточку на странице
    this.$.insertAdjacentHTML('beforeend', template)
})
Не забываем реализовать обработку ошибок:
this.eventSource.addEventListener('error', e => {
    this.eventSource.close()
    this.eventLog.textContent = `Got an error: ${e}`
    this.changeDisabled()
}, {once: true})
Наконец, инициализируем приложение:
const app = new App('main')
Полный код клиента:
SPL
const getTemplate = user => `
<div class="card">
    <div class="row">
        <div class="col-md-4">
            <img src="${user.img}" class="card-img" alt="user-photo">
        </div>
        <div class="col-md-8">
            <div class="card-body">
                <h5 class="card-title">${user.id !== null ? `Id: ${user.id}` : `User hasn't id`}</h5>
                <p class="card-text">Name: ${user.name}</p>
                <p class="card-text">Username: ${user.username}</p>
                <p class="card-text">Email: ${user.email}</p>
                <p class="card-text">Age: ${user.age}</p>
            </div>
        </div>
    </div>
</div>
`
class App {
    constructor(selector) {
        this.$ = document.querySelector(selector)
        this.#init()
    }
    #init() {
        this.startBtn = this.$.querySelector('[data-type="start-btn"]')
        this.stopBtn = this.$.querySelector('[data-type="stop-btn"]')
        this.eventLog = this.$.querySelector('[data-type="event-log"]')
        this.clickHandler = this.clickHandler.bind(this)
        this.$.addEventListener('click', this.clickHandler)
    }
    clickHandler(e) {
        if (e.target.tagName === 'BUTTON') {
            const {
                type
            } = e.target.dataset
            if (type === 'start-btn') {
                this.startEvents()
            } else if (type === 'stop-btn') {
                this.stopEvents()
            }
            this.changeDisabled()
        }
    }
    changeDisabled() {
        if (this.stopBtn.disabled) {
            this.stopBtn.disabled = false
            this.startBtn.disabled = true
        } else {
            this.stopBtn.disabled = true
            this.startBtn.disabled = false
        }
    }
    startEvents() {
        this.eventSource = new EventSource('http://localhost:3000/getUsers')
        this.eventLog.textContent = 'Accepting data from the server.'
        this.eventSource.addEventListener('message', e => {
            if (e.lastEventId === '-1') {
                this.eventSource.close()
                this.eventLog.textContent = 'End of stream from the server.'
                this.changeDisabled()
            }
        }, {once: true})
        this.eventSource.addEventListener('randomUser', e => {
            const userData = JSON.parse(e.data)
            console.log(userData)
            const {
                id,
                name,
                login,
                email,
                dob,
                picture
            } = userData
            const i = id.value
            const fullName = `${name.first} ${name.last}`
            const username = login.username
            const age = dob.age
            const img = picture.large
            const user = {
                id: i,
                name: fullName,
                username,
                email,
                age,
                img
            }
            const template = getTemplate(user)
            this.$.insertAdjacentHTML('beforeend', template)
        })
        this.eventSource.addEventListener('error', e => {
            this.eventSource.close()
            this.eventLog.textContent = `Got an error: ${e}`
            this.changeDisabled()
        }, {once: true})
    }
    stopEvents() {
        this.eventSource.close()
        this.eventLog.textContent = 'Event stream closed by client.'
    }
}
const app = new App('main')
На всякий случай перезапускаем сервер. Открываем http://localhost:3000. Нажимаем на кнопку «Start»:

Начинаем получать данные от сервера и рендерить карточки пользователей.
Если нажать на кнопку «Stop», отправка данных будет приостановлена:

Снова нажимаем «Start», отправка данных продолжается.
При достижении лимита (10 пользователей) сервер отправляет идентификатор со значением -1 и закрывает соединение. Клиент, в свою очередь, также закрывает поток событий:

Как видите, SSE очень похож на веб-сокеты. Недостатком является однонаправленность сообщений: сообщения могут отправляться только сервером. Преимущество состоит в автоматическом переподключении и простоте реализации.
Поддержка данной технологии на сегодняшний день составляет 95%:

Надеюсь, статья вам понравилась. Благодарю за внимание.
===========
 Источник:
habr.com
===========
Похожие новости:
- [Программирование, Изучение языков] Hi Programming Language
 
- [Схемотехника, Электроника для начинающих, Производство и разработка электроники, Энергия и элементы питания] Электробезопасность оптических изоляторов в условиях возможных отказов (перевод)
 
- [Разработка веб-сайтов, PHP, Laravel] Laravel–Дайджест (14–20 сентября 2020)
 
- [Разработка игр, Дизайн игр] Анимация в KAPIA. «Лучше один день потерять, чтобы потом за пять минут долететь»
 
- [JavaScript, PostgreSQL, Программирование, Алгоритмы] Immutable Trie: найди то, не знаю что, но быстро, и не мусори
 
- Выпуск Vue.js 3.0.0, фреймворка для создания пользовательских интерфейсов
 
- [Разработка игр, DevOps, Игры и игровые приставки] Индюшатина, как ее готовят
 
- [JavaScript, Node.JS] Подключение и настройка TradingView графиков
 
- [Python, Программирование, Визуальное программирование] Опыт проведения городской школьной олимпиады по программированию
 
- [ReactJS, JavaScript] React 17: Ничего нового? (перевод)
 
Теги для поиска: #_javascript, #_programmirovanie (Программирование), #_razrabotka_vebsajtov (Разработка веб-сайтов), #_javascript, #_programmirovanie (программирование), #_razrabotka (разработка), #_serversent_events, #_sse, #_eventsource, #_javascript, #_programmirovanie (
Программирование
), #_razrabotka_vebsajtov (
Разработка веб-сайтов
)
                                        
                                        
                                        
                                     
                                    
                                    
                                                                    
                                                                                             
                         
                        
                            
                                                                    
                                                             
                         
                    
                    
                
                
            
        
    
    
    
    
    
            
    
            
    
        
    
    
        
                        Вы не можете начинать темы
Вы не можете отвечать на сообщения
Вы не можете редактировать свои сообщения
Вы не можете удалять свои сообщения
Вы не можете голосовать в опросах
Вы не можете прикреплять файлы к сообщениям
Вы не можете скачивать файлы
    
    
        
        Текущее время: 04-Ноя 08:15
Часовой пояс: UTC + 5 
            
    
                
| Автор | Сообщение | 
|---|---|
| 
                                
                                
                                                                                                            news_bot ®
                                                                        
                                                                                                                                                 
                                                                            
                                                                                                                
                                            Стаж: 7 лет 8 месяцев                                          | 
                            |
                                ![]() Доброго времени суток, друзья! В этом туториале мы рассмотрим Server Sent Events: встроенный класс EventSource, который позволяет поддерживать соединение с сервером и получать от него события. О том, что такое SSE и для чего он используется можно почитать здесь. Что конкретно мы будем делать? Мы напишем простой сервер, который будет по запросу клиента отправлять ему данные 10 случайных пользователей, а клиент с помощью этих данных будет формировать карточки пользователей. Сервер будет реализован на Node.js, клиент — на JavaScript. Для стилизации будет использоваться Bootstrap, в качестве API — Random User Generator. Код проекта находится здесь. Поиграть с кодом можно здесь. Если вам это интересно, прошу следовать за мной. Подготовка Создаем директорию sse-tut: mkdir sse-tut 
Заходим в нее и инициализируем проект: cd sse-tut 
yarn init -y // или npm init -y Устанавливаем axios: yarn add axios 
// или npm i axios axios будет использоваться для получения данных пользователей. Редактируем package.json: "main": "server.js", 
"scripts": { "start": "node server.js" }, Структура проекта: sse-tut 
--node_modules --client.js --index.html --package.json --server.js --yarn.lock Содержание index.html: <head> 
<!-- Bootstrap CSS --> <link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.5.2/css/bootstrap.min.css" integrity="sha384-JcKb8q3iqJ61gNV9KGb8thSsNjpSL0n8PARn9HuZOnIxN0hoP+VmmDGMN5t9UJ0Z" crossorigin="anonymous"> <style> .card { margin: 0 auto; max-width: 512px; } img { margin: 1rem; max-width: 320px; } p { margin: 1rem; } </style> </head> <body> <main class="container text-center"> <h1>Server-Sent Events Tutorial</h1> <button class="btn btn-primary" data-type="start-btn">Start</button> <button class="btn btn-danger" data-type="stop-btn" disabled>Stop</button> <p data-type="event-log"></p> </main> <script src="client.js"></script> </body> Сервер Приступаем к реализации сервера. Открываем server.js. Подключаем http и axios, определяем порт: const http = require('http') 
const axios = require('axios') const PORT = process.env.PORT || 3000 Создаем функцию получения данных пользователя: const getUserData = async () => { 
const response = await axios.get('https://randomuser.me/api') // проверяем полученные данные console.log(response) return response.data.results[0] } Создаем счетчик количества отправленных пользователей: let i = 1 
Пишем функцию отправки данных клиенту: const sendUserData = (req, res) => { 
// статус ответа - 200 ок // соединение должно оставаться открытым // тип содержимого - поток событий // не кэшировать res.writeHead(200, { Connection: 'keep-alive', 'Content-Type': 'text/event-stream', 'Cache-Control': 'no-cache' }) // данные будут отправляться каждые 2 секунды const timer = setInterval(async () => { // если отправлено 10 пользователей if (i > 10) { // останавливаем таймер clearInterval(timer) // сообщаем о том, что было отправлено 10 пользователей console.log('10 users has been sent.') // отправляем клиенту идентификатор со значением -1 // для того, чтобы клиент закрыл соединение res.write('id: -1\ndata:\n\n') // закрываем соединение res.end() return } // получаем данные const data = await getUserData() // записываем данные в ответ // event - название события // id - идентификатор события; используется при повторном подключении // retry - время повторного подключения // data - данные res.write(`event: randomUser\nid: ${i}\nretry: 5000\ndata: ${JSON.stringify(data)}\n\n`) // сообщаем о том, что данные отправлены console.log('User data has been sent.') // увеличиваем значение счетчика i++ }, 2000) // обрабатываем закрытие соединения клиентом req.on('close', () => { clearInterval(timer) res.end() console.log('Client closed the connection.') }) } Создаем и запускаем сервер: http.createServer((req, res) => { 
// обязательный заголовок для преодоления CORS res.setHeader('Access-Control-Allow-Origin', '*') // если адрес запроса - getUser if (req.url === '/getUsers') { // отправляем данные sendUserData(req, res) } else { // иначе, сообщаем о том, что запрашиваемая страница не найдена, // и закрываем соединение res.writeHead(404) res.end() } }).listen(PORT, () => console.log('Server ready.')) Полный код сервера:SPLconst http = require('http') 
const axios = require('axios') const PORT = process.env.PORT || 3000 const getUserData = async () => { const response = await axios.get('https://randomuser.me/api') return response.data.results[0] } let i = 1 const sendUserData = (req, res) => { res.writeHead(200, { Connection: 'keep-alive', 'Content-Type': 'text/event-stream', 'Cache-Control': 'no-cache' }) const timer = setInterval(async () => { if (i > 10) { clearInterval(timer) console.log('10 users has been sent.') res.write('id: -1\ndata:\n\n') res.end() return } const data = await getUserData() res.write(`event: randomUser\nid: ${i}\nretry: 5000\ndata: ${JSON.stringify(data)}\n\n`) console.log('User data has been sent.') i++ }, 2000) req.on('close', () => { clearInterval(timer) res.end() console.log('Client closed the connection.') }) } http.createServer((req, res) => { res.setHeader('Access-Control-Allow-Origin', '*') if (req.url === '/getUsers') { sendUserData(req, res) } else { res.writeHead(404) res.end() } }).listen(PORT, () => console.log('Server ready.')) Выполняем команду yarn start или npm start. В терминале появляется сообщение «Server ready.». Открываем http://localhost:3000: ![]() С сервером закончили, переходим к клиентской части приложения. Клиент Открываем файл client.js. Создаем функцию генерации шаблона пользовательской карточки: const getTemplate = user => ` 
<div class="card"> <div class="row"> <div class="col-md-4"> <img src="${user.img}" class="card-img" alt="user-photo"> </div> <div class="col-md-8"> <div class="card-body"> <h5 class="card-title">${user.id !== null ? `Id: ${user.id}` : `User hasn't id`}</h5> <p class="card-text">Name: ${user.name}</p> <p class="card-text">Username: ${user.username}</p> <p class="card-text">Email: ${user.email}</p> <p class="card-text">Age: ${user.age}</p> </div> </div> </div> </div> ` Шаблон генерируется с использованием следующих данных: идентификатор пользователя (если имеется), имя, логин, адрес электронной почты и возраст пользователя. Начинаем реализовывать основной функционал: class App { 
constructor(selector) { // основной элемент - контейнер this.$ = document.querySelector(selector) // запускаем частный метод this.#init() } #init() { this.startBtn = this.$.querySelector('[data-type="start-btn"]') this.stopBtn = this.$.querySelector('[data-type="stop-btn"]') // контейнер для сообщений о событиях this.eventLog = this.$.querySelector('[data-type="event-log"]') // устанавливаем контекст для обработчика this.clickHandler = this.clickHandler.bind(this) // делегируем обработку события this.$.addEventListener('click', this.clickHandler) } clickHandler(e) { // если кликнули по кнопке if (e.target.tagName === 'BUTTON') { // получаем тип кнопки // и либо начинаем получать события от сервера, // либо закрываем соединение const { type } = e.target.dataset if (type === 'start-btn') { this.startEvents() } else if (type === 'stop-btn') { this.stopEvents() } // управление состоянием кнопок this.changeDisabled() } } changeDisabled() { if (this.stopBtn.disabled) { this.stopBtn.disabled = false this.startBtn.disabled = true } else { this.stopBtn.disabled = true this.startBtn.disabled = false } } //... Сначала реализуем закрытие соединения: stopEvents() { 
this.eventSource.close() // сообщаем о том, что соединение закрыто пользователем this.eventLog.textContent = 'Event stream closed by client.' } Переходим к открытию потока событий: startEvents() { 
// создаем экземпляр для получения данных по запросу на указанный адрес this.eventSource = new EventSource('http://localhost:3000/getUsers') // сообщаем о том, что соединение открыто this.eventLog.textContent = 'Accepting data from the server.' // обрабатываем получение от сервера идентификатора со значением -1 this.eventSource.addEventListener('message', e => { if (e.lastEventId === '-1') { // закрываем соединение this.eventSource.close() // сообщаем об этом this.eventLog.textContent = 'End of stream from the server.' this.changeDisabled() } // мы можем получить такой идентификатор лишь раз }, {once: true}) } Обрабатываем кастомное событие «randomUser»: this.eventSource.addEventListener('randomUser', e => { 
// парсим полученные данные const userData = JSON.parse(e.data) // проверяем их console.log(userData) // извлекаем данные с помощью деструктуризации const { id, name, login, email, dob, picture } = userData // продолжаем формировать данные, необходимые для генерации пользовательской карточки const i = id.value const fullName = `${name.first} ${name.last}` const username = login.username const age = dob.age const img = picture.large const user = { id: i, name: fullName, username, email, age, img } // генерируем шаблон const template = getTemplate(user) // рендерим карточку на странице this.$.insertAdjacentHTML('beforeend', template) }) Не забываем реализовать обработку ошибок: this.eventSource.addEventListener('error', e => { 
this.eventSource.close() this.eventLog.textContent = `Got an error: ${e}` this.changeDisabled() }, {once: true}) Наконец, инициализируем приложение: const app = new App('main') 
Полный код клиента:SPLconst getTemplate = user => ` 
<div class="card"> <div class="row"> <div class="col-md-4"> <img src="${user.img}" class="card-img" alt="user-photo"> </div> <div class="col-md-8"> <div class="card-body"> <h5 class="card-title">${user.id !== null ? `Id: ${user.id}` : `User hasn't id`}</h5> <p class="card-text">Name: ${user.name}</p> <p class="card-text">Username: ${user.username}</p> <p class="card-text">Email: ${user.email}</p> <p class="card-text">Age: ${user.age}</p> </div> </div> </div> </div> ` class App { constructor(selector) { this.$ = document.querySelector(selector) this.#init() } #init() { this.startBtn = this.$.querySelector('[data-type="start-btn"]') this.stopBtn = this.$.querySelector('[data-type="stop-btn"]') this.eventLog = this.$.querySelector('[data-type="event-log"]') this.clickHandler = this.clickHandler.bind(this) this.$.addEventListener('click', this.clickHandler) } clickHandler(e) { if (e.target.tagName === 'BUTTON') { const { type } = e.target.dataset if (type === 'start-btn') { this.startEvents() } else if (type === 'stop-btn') { this.stopEvents() } this.changeDisabled() } } changeDisabled() { if (this.stopBtn.disabled) { this.stopBtn.disabled = false this.startBtn.disabled = true } else { this.stopBtn.disabled = true this.startBtn.disabled = false } } startEvents() { this.eventSource = new EventSource('http://localhost:3000/getUsers') this.eventLog.textContent = 'Accepting data from the server.' this.eventSource.addEventListener('message', e => { if (e.lastEventId === '-1') { this.eventSource.close() this.eventLog.textContent = 'End of stream from the server.' this.changeDisabled() } }, {once: true}) this.eventSource.addEventListener('randomUser', e => { const userData = JSON.parse(e.data) console.log(userData) const { id, name, login, email, dob, picture } = userData const i = id.value const fullName = `${name.first} ${name.last}` const username = login.username const age = dob.age const img = picture.large const user = { id: i, name: fullName, username, email, age, img } const template = getTemplate(user) this.$.insertAdjacentHTML('beforeend', template) }) this.eventSource.addEventListener('error', e => { this.eventSource.close() this.eventLog.textContent = `Got an error: ${e}` this.changeDisabled() }, {once: true}) } stopEvents() { this.eventSource.close() this.eventLog.textContent = 'Event stream closed by client.' } } const app = new App('main') На всякий случай перезапускаем сервер. Открываем http://localhost:3000. Нажимаем на кнопку «Start»: ![]() Начинаем получать данные от сервера и рендерить карточки пользователей. Если нажать на кнопку «Stop», отправка данных будет приостановлена: ![]() Снова нажимаем «Start», отправка данных продолжается. При достижении лимита (10 пользователей) сервер отправляет идентификатор со значением -1 и закрывает соединение. Клиент, в свою очередь, также закрывает поток событий: ![]() Как видите, SSE очень похож на веб-сокеты. Недостатком является однонаправленность сообщений: сообщения могут отправляться только сервером. Преимущество состоит в автоматическом переподключении и простоте реализации. Поддержка данной технологии на сегодняшний день составляет 95%: ![]() Надеюсь, статья вам понравилась. Благодарю за внимание. =========== Источник: habr.com =========== Похожие новости: 
 Программирование ), #_razrabotka_vebsajtov ( Разработка веб-сайтов )  | 
                        |
Вы не можете начинать темы
Вы не можете отвечать на сообщения
Вы не можете редактировать свои сообщения
Вы не можете удалять свои сообщения
Вы не можете голосовать в опросах
Вы не можете прикреплять файлы к сообщениям
Вы не можете скачивать файлы
    Вы не можете отвечать на сообщения
Вы не можете редактировать свои сообщения
Вы не можете удалять свои сообщения
Вы не можете голосовать в опросах
Вы не можете прикреплять файлы к сообщениям
Вы не можете скачивать файлы
Текущее время: 04-Ноя 08:15
Часовой пояс: UTC + 5