В настоящее время у нас есть приложение VueJS
, и я рассматриваю возможность его переноса на Cycle.js (первый крупный проект).
Я понимаю, что в Cycle.JS у нас есть SI и SO для драйверов (используя адаптацию()); естественно, реализация WebSocket подходит для этого, поскольку она имеет эффекты чтения и записи.
Мы используем Phoenix (Elixir) в качестве нашего бэкэнда, используя каналы для мягкого общения в реальном времени. Наша клиентская библиотека WS называется Phoenix здесьhttps://www.npmjs.com/package/phoenix а>.
пример на Cycle.js.org идеален, если вы знаете, как подключить.
В нашем случае мы аутентифицируемся с помощью конечной точки REST, которая возвращает токен (JWT), который используется для инициализации WebSocket (параметр токена). Этот токен нельзя просто передать в драйвер, так как драйвер инициализируется при запуске приложения Cycle.js.
Пример (не фактический код) того, что у нас есть сейчас (в нашем приложении VueJS):
// Code ommited for brevity
socketHandler = new vueInstance.$phoenix.Socket(FQDN, {
_token: token
});
socketHandler.onOpen(() => VueBus.$emit('SOCKET_OPEN'));
//...... Vue component (example)
VueBus.$on('SOCKET_OPEN', function () {
let chan = VueStore.socketHandler.channel('PRIV_CHANNEL', {
_token: token
});
chan.join()
.receive('ok', () => {
//... code
})
})
В приведенном выше примере у нас есть хранилище Vuex
для глобального состояния (сокет и т. д.), централизованная шина сообщений (приложение Vue) для связи между компонентами и настройками канала, которые поступают из созданного экземпляра сокета Phoenix.
Наша настройка канала основана на аутентифицированном сокет-соединении, которое требует аутентификации для присоединения к этому конкретному каналу.
Вопрос в том, возможно ли это вообще с Cycle.js?
- Инициализировать соединение WebSocket с параметрами токена из вызова REST (ответ токена JWT) — мы реализовали это частично
- Создавать каналы на основе этого сокета и токена (потоки каналов от драйвера?)
- Доступ к многоканальным потокам (предполагаю, что это может работать как sources.HTTP.select(CATEGORY))
У нас здесь зависимость 1: N, что, я не уверен, возможно с драйверами.
Заранее спасибо,
Обновление от 17 декабря 2018 г.
По сути, я пытаюсь имитировать следующее (из Cycle.js.org):
Драйвер принимает приемник для выполнения эффектов записи (отправки сообщений по определенным каналам), но также может возвращать источник; это означает, что есть два асинхронных потока? Это означает, что создание сокета во время выполнения может привести к тому, что один поток получит доступ к «сокету» до того, как он будет создан; см. комментарии во фрагменте ниже.
import {adapt} from '@cycle/run/lib/adapt';
function makeSockDriver(peerId) {
// This socket may be created at an unknown period
//let socket = new Sock(peerId);
let socket = undefined;
// Sending is perfect
function sockDriver(sink$) {
sink$.addListener({
next: listener => {
sink$.addListener({
next: ({ channel, data }) => {
if(channel === 'OPEN_SOCKET' && socket === null) {
token = data;
// Initialising the socket
socket = new phoenix.Socket(FQDN, { token });
socketHandler.onOpen(() => listener.next({
channel: 'SOCKET_OPEN'
}));
} else {
if(channels[channel] === undefined) {
channels[channel] = new Channel(channel, { token });
}
channels[channel].join()
.receive('ok', () => {
sendData(data);
});
}
}
});
},
error: () => {},
complete: () => {},
});
const source$ = xs.create({
start: listener => {
sock.onReceive(function (msg) {
// There is no guarantee that "socket" is defined here, as this may fire before the socket is actually created
socket.on('some_event'); // undefined
// This works however because a call has been placed back onto the browser stack which probably gives the other blocking thread chance to write to the local stack variable "socket". But this is far from ideal
setTimeout(() => socket.on('some_event'));
});
},
stop: () => {},
});
return adapt(source$);
}
return sockDriver;
}
Ян ван Брюгге, решение, которое вы предоставили, идеально (спасибо), за исключением того, что у меня возникли проблемы с ответной частью. См. приведенный выше пример.
Например, то, что я пытаюсь достичь, выглядит примерно так:
// login component
return {
DOM: ...
WS: xs.of({
channel: "OPEN_CHANNEL",
data: {
_token: 'Bearer 123'
}
})
}
//////////////////////////////////////
// Some authenticated component
// Intent
const intent$ = sources.WS.select(CHANNEL_NAME).startWith(null)
// Model
const model$ = intent$.map(resp => {
if (resp.some_response !== undefined) {
return {...}; // some model
}
return resp;
})
return {
DOM: model$.map(resp => {
// Use response from websocket to create UI of some sort
})
}