导致争用情况的环回事件流

时间:2019-05-18 00:58:27

标签: javascript loopbackjs event-stream

我的总体目标是监视数据库中的任何更改,并将这些更改自动广播给连接到我的网站的任何用户。 我看到的问题是我有一个动作触发了对数据库的发布请求,然后事件流同时被触发,因为我正在观看的模型已更改。这将导致初始操作完成,并且事件流触发的操作在执行之前就被中断了。

这是在我的数据库中创建新博客文章条目时触发的第一个操作

export const topicSubmit = (date, newTopic, newTopicBody, memberId, name) => {
    return {
        type: 'TOPIC_SUBMIT',
        payload: axios({
            method: 'post',
            url: `/api/blogPosts`,
            data: {
                "blogTitle": newTopic,
                "blogBody": newTopicBody,
                "date": date,
                "upVotes": 0,
                "numComments": 0,
                "voteNames": [],
                "memberId": memberId,
                "steamNameId": name
            }
        })
            .then(response => {
                return response.data
            })
            .catch(err => err)
    }
}

// this is the boot script that creates the change stream

var es = require('event-stream');
module.exports = function (app) {
    console.log('realtime boot script')
    var BlogPost = app.models.BlogPost;
    BlogPost.createChangeStream(function (err, changes) {
        changes.pipe(es.stringify()).pipe(process.stdout);
    });
}

// this is the event listener on my front end that will dispatch all
// changes made in my database to my front end

componentDidMount() {
        const { dispatch } = this.props;
          let urlToChangeStream = '/api/blogPosts/change-stream?_format=event-stream';
          let src = new EventSource(urlToChangeStream);
          src.addEventListener('data', function (msg) {
              let data = JSON.parse(msg.data);
          dispatch(liveChangeBlogs(data))
          });

我期望在事件监听器分派“ liveChangeBlogs”操作之前,“ TOPIC_SUBMIT”操作应返回执行状态 这是我在回送事件流https://loopback.io/doc/en/lb3/Realtime-server-sent-events.html

上找到的文档

2 个答案:

答案 0 :(得分:0)

I am expecting that the 'TOPIC_SUBMIT' action should return fulfilled before the 'liveChangeBlogs' action is dispatched by the event listener

I am afraid this is not possible. Even if the LoopBack server withheld sending of the event-stream entry until a response to the POST request was sent, it still won't be able to guarantee that the client will receive (and process!) the response to the POST request before it processes the event-stream entry.

My recommendation is to keep track of in-flight requests in your client and drop event-stream entries for changes made by the same client.

Something along the following lines:

const pendingRequests = [];

export const topicSubmit = (date, newTopic, newTopicBody, memberId, name) => {
    const data = {
                "blogTitle": newTopic,
                "blogBody": newTopicBody,
                "date": date,
                "upVotes": 0,
                "numComments": 0,
                "voteNames": [],
                "memberId": memberId,
                "steamNameId": name
            };
    const entry = {type: 'CREATE', data}
    pendingRequests.push(entry);

    return {
        type: 'TOPIC_SUBMIT',
        payload: axios({
            method: 'post',
            url: `/api/blogPosts`,
            data,

        })
            .then(response => {
                return response.data
            })
            .catch(err => err)
            .finally(() => {
              // remove the entry from pending requests
              const ix = pendingRequests.indexOf(entry);
              if (ix > -1) pendingRequests.splice(ix, 1);
            })
    }
}

// this is the event listener on my front end that will dispatch all
// changes made in my database to my front end
// (...)
          src.addEventListener('data', function (msg) {
              let data = JSON.parse(msg.data);
              const ours = pendingRequests.find(it => {
                it.type === data.type && /* check fields that uniquely identify model instance and/or the change being made */
              });
              if (ours) {
                // this event was triggered by us, discard it
                return; 
              }
          dispatch(liveChangeBlogs(data))
          });

答案 1 :(得分:0)

我最终使用Redux Thunk解决了这个问题,为我的componentDidMount添加了setTimeout和闭包。 topicSubmit操作和启动脚本未更改。不确定setTimeout是否是正确的方法,但这是我想到解决竞赛案例的唯一方法。

 componentDidMount() {
        const { dispatch } = this.props;
        const newBlog = this.handleNewBlog;
        let urlToChangeStream = '/api/blogPosts/change-stream?_format=event-stream';
        let src = new EventSource(urlToChangeStream);
        src.addEventListener('data', function (msg) {
            newBlog(msg)
        });

        const newThread = this.handleNewThread;
        let urlToChangeStream2 = '/api/threads/change-stream?_format=event-stream';
        let src2 = new EventSource(urlToChangeStream2);
        src2.addEventListener('data', function (msg) {
            newThread(msg)
        });
        dispatch(getBlogs());
    }

  handleNewBlog(msg) {
        const { dispatch } = this.props;
        let data = JSON.parse(msg.data);
        if(data.data == undefined) {
            setTimeout(() => {
                dispatch(getBlogs());
            }, 1000);
        } else {
            setTimeout(() => {
                dispatch(liveChangeBlogs(data));
            }, 1000);
        }
    }

 handleNewThread(msg) {
        const { dispatch, viewingThreadId } = this.props;
        let data2 = JSON.parse(msg.data);
        console.log('data2: ', data2)
        if (data2.type == 'remove') {
            return dispatch(getThreadsById(viewingThreadId))
        }
        let id = data2.data.blogPostId
        setTimeout(() => {
            if (viewingThreadId === id) {
                dispatch(getThreadsById(id));
            } else {
                return
            }
        }, 1000);
    }