簡體   English   中英

Cycle.js-驅動程序-PhoenixJS(Websockets)

[英]Cycle.js - Driver - PhoenixJS (Websockets)

當前,我們有一個VueJS應用程序,我正在考慮將其遷移到Cycle.js(第一個主要項目)。

我了解在Cycle.JS中,我們有用於驅動程序的SI和SO(使用Adapt()); 很自然,WebSocket實現適合於此,因為它既具有讀取效果,又具有寫入效果。

我們使用Phoenix(Elixir)作為后端,使用Channels進行軟實時通信。 我們的客戶端WS庫是Phoenix,位於https://www.npmjs.com/package/phoenix

例子在Cycle.js.org是完美的,如果你知道如何連接。

在我們的例子中,我們使用REST端點進行身份驗證,該端點返回用於初始化WebSocket(令牌參數)的令牌(JWT)。 該令牌不能簡單地傳遞到驅動程序中,因為在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 Socket。

我們的頻道設置依賴於經過身份驗證的Socket連接,該連接本身需要身份驗證才能加入該特定頻道。

問題是,使用Cycle.js甚至可能嗎?

  1. 使用來自REST調用的令牌參數初始化WebSocket連接(JWT令牌響應)- 我們已部分實現了這一點
  2. 基於該套接字和令牌創建通道( 通道從驅動程序流傳輸?
  3. 訪問多個通道流( 我假設它可能像source.HTTP.select(CATEGORY)一樣工作

我們這里有一個1:N的依賴關系,我不確定驅動程序是否可能。

先感謝您,

更新@ 17/12/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;
}

揚·范·布魯格(Jan vanBrügge),您提供的解決方案非常完美(謝謝),但我在響應部分遇到了麻煩。 請參見上面的示例。

例如,我想要達到的目標是這樣的:

// 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
    })
}

首先,是的,這可以通過驅動程序實現,我的建議是使驅動程序看起來像HTTP驅動程序。

首先,要准備一些粗略的偽代碼,在其中我可以解釋所有內容,我可能對您的問題有誤解,所以這可能是錯誤的。

interface WebsocketMessage {
    channel: string;
    data: any;
}

function makeWebSocketDriver() {
    let socket = null;
    let token = null;
    let channels = {}
    return function websocketDriver(sink$: Stream<WebsocketMessage> {
        return xs.create({
            start: listener => {
                sink$.addListener({
                    next: ({ channel, data }) => {
                        if(channel === 'OPEN_SOCKET' && socket === null) {
                            token = data;
                            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);
                                });
                        }
                    }
                });
            }
        });
    };
}

這將是這種驅動器的粗略結構。 您會看到它等待帶有令牌的消息,然后打開套接字。 它還會跟蹤打開的通道,並根據消息的類別在這些通道中進行發送/接收。 此方法僅要求所有通道都具有唯一的名稱,我不確定您的通道協議在這方面的工作方式或具體需要什么。

我希望這足以使您入門,如果您弄清楚通道發送/接收和套接字的API,我也許可以提供更多幫助。 也總是歡迎您在我們的gitter頻道中提問

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM