admin管理员组文章数量:1406000
I'm attempting to replicate this demo using RxJS. The demo is a small application, where the user controls a robot. The robot can move forwards or backwards, rotate left or right, and pick up or drop an item. The user can queue mands (such as "Forward", "rotate"), and the mands in the queue are executed when the user clicks on the "Execute"-button. The user can also undo mands that have been already executed.
Traditionally this application would be quite easy to implement using a queue for the mands that have not been executed yet. Executed mands are pushed into a stack, and whenever the undo-button is pressed, the top mand is popped and undone.
I'm able to "collect" the mands and execute them by doing this:
var id = 0;
var add = Rx.Observable.fromEvent($("#add"), 'click').map(function(){
var ret = "Command_"+id;
id++;
return ret
})
var invoke = Rx.Observable.fromEvent($("#invoke"), 'click')
var invokes = add.buffer(invoke)
The buffer() method transforms the stream into a stream of arrays. I can either subscribe to the invokes stream and get arrays of mands:
invokes.subscribe(function(mand_array){...})
or I can create a Rx.Subject() where I just push the mands one by one:
var invoked_mands = new Rx.Subject()
invokes.subscribe(function(mand_array){
for(var i=0; i < mand_array.length; i++){
invoked_mands.onNext(mand_array[i])
}
});
invoked_mands.subscribe(function(mand){ ...});
To be honest I have no idea which approach would be better, but I then again I don't know if that's even too relevant to me right now. I've been trying to figure out how to implement the undo functionality, but I have absolutely no idea how to do it.
In my mind it would have to be something like this (sorry for the formatting):
-c1---c2-c3--------->
----------------u---u-> ("u" = clicking the undo button)
----------------c3--c2> (get mands from newest to oldest, call the undo() method)
So my question is twofold:
- Is my approach of collecting the mands good?
- How can I implement the undo feature?
EDIT: I'm paring transformative and reactive styles, and I'm implementing this demo using both. Therefore I'd like to stick to using Rx* features as much as possible.
I'm attempting to replicate this demo using RxJS. The demo is a small application, where the user controls a robot. The robot can move forwards or backwards, rotate left or right, and pick up or drop an item. The user can queue mands (such as "Forward", "rotate"), and the mands in the queue are executed when the user clicks on the "Execute"-button. The user can also undo mands that have been already executed.
Traditionally this application would be quite easy to implement using a queue for the mands that have not been executed yet. Executed mands are pushed into a stack, and whenever the undo-button is pressed, the top mand is popped and undone.
I'm able to "collect" the mands and execute them by doing this:
var id = 0;
var add = Rx.Observable.fromEvent($("#add"), 'click').map(function(){
var ret = "Command_"+id;
id++;
return ret
})
var invoke = Rx.Observable.fromEvent($("#invoke"), 'click')
var invokes = add.buffer(invoke)
The buffer() method transforms the stream into a stream of arrays. I can either subscribe to the invokes stream and get arrays of mands:
invokes.subscribe(function(mand_array){...})
or I can create a Rx.Subject() where I just push the mands one by one:
var invoked_mands = new Rx.Subject()
invokes.subscribe(function(mand_array){
for(var i=0; i < mand_array.length; i++){
invoked_mands.onNext(mand_array[i])
}
});
invoked_mands.subscribe(function(mand){ ...});
To be honest I have no idea which approach would be better, but I then again I don't know if that's even too relevant to me right now. I've been trying to figure out how to implement the undo functionality, but I have absolutely no idea how to do it.
In my mind it would have to be something like this (sorry for the formatting):
-c1---c2-c3--------->
----------------u---u-> ("u" = clicking the undo button)
----------------c3--c2> (get mands from newest to oldest, call the undo() method)
So my question is twofold:
- Is my approach of collecting the mands good?
- How can I implement the undo feature?
EDIT: I'm paring transformative and reactive styles, and I'm implementing this demo using both. Therefore I'd like to stick to using Rx* features as much as possible.
Share Improve this question edited Mar 23, 2015 at 17:40 T.Kaukoranta asked Mar 23, 2015 at 14:56 T.KaukorantaT.Kaukoranta 42510 silver badges27 bronze badges 2- There’s a similar newer question with a very succint answer out there. – tomekwi Commented May 16, 2015 at 13:34
- It turns out that the answer solves a slightly different problem though. – tomekwi Commented May 16, 2015 at 14:06
3 Answers
Reset to default 4You have to go ahead and maintain state for the undo stack. I think your approach to collecting mands is reasonable. If you keep your Subject
you can sort of decouple the undo functionality from the mand execution by making another subscription to the subject:
var undoQueue = [];
invoked_mands.subscribe(function (c) { undoQueue.unshift(c); });
Rx.Observable
.fromEvent($("#undo"), "click")
.map(function () { return undoQueue.pop(); })
.filter(function (mand) { return mand !== undefined; })
.subscribe(function (mand) { /* undo mand */ });
Edit: Using only Rx without a mutable array. This seems unnecessarily convoluted, but oh well, it is functional. We use scan
to maintain the undo queue, and emit a tuple with the current queue along with whether a undo mand should be executed. We merge executed mands with undo events. Execute mands add to the queue, undo events pop from the queue.
var undo = Rx.Observable
.fromEvent($("#undo"), "click")
.map(function () { return "undo"; });
invoked_mands
.merge(undo)
.scan({ undoCommand: undefined, q: [] }, function (acc, value) {
if (value === "undo") {
return { undoCommand: acc.q[0], q: acc.q.slice(1) };
}
return { undoCommand: undefined, q: [value].concat(acc.q) };
})
.pluck("undoCommand")
.filter(function (c) { return c !== undefined })
.subscribe(function (undoCommand) { ... });
I just created something similar, although a bit more plex. Maybe it helps someone.
// Observable for all keys
const keypresses = Rx.Observable
.fromEvent(document, 'keydown')
// Undo key bination was pressed
// mapped to function that undoes the last accumulation of pressed keys
const undoPressed = keypresses
.filter(event => event.metaKey && event.key === 'z')
.map(() => (acc) => acc.slice(0, isEmpty(last(acc)) && -2 || -1).concat([[]]))
// a 'simple' key was pressed
const inputChars = keypresses
.filter(event => !event.altKey && !event.metaKey && !event.ctrlKey)
.map(get('key'))
.filter(key => key.length === 1)
// the user input, respecting undo
const input = inputChars
.map((char) => (acc) =>
acc.slice(0, -1).concat(
acc.slice(-1).pop().concat(char)
)
) // map input keys to functions that append them to the current list
.merge(undoPressed)
.merge(
inputChars
.auditTime(1000)
.map(() => (acc) => isEmpty(last(acc)) && acc || acc.concat([[]]))
) // creates functions, that start a new accumulator 1 sec after the first key of a stroke was pressed
.scan(
(acc, f) => f(acc),
[[]],
) // applies the merged functions to a list of accumulator strings
.map(join('')) // join string
.distinctUntilChanged() // ignore audit event, because it doesn't affect the current string
It's great to diy first. Reinventing the wheel es with deeper understanding of concepts & makey you a brillant engineer, in real life sometimes you just gotta benefit from an already existing package to:
- save time (efficiency)
- ensure stability (munity approved)
- less code (maintainability)
- generic package (reusability)
rx-undoable
Update:
I'm more happy with Zundo now, also due to code review and usage of TypeScript and < 1kb (no offense to other lib, it works, so it's great too)
Github: 'zundo'
本文标签: javascriptSimulating a command queue and undo stack with RxJSStack Overflow
版权声明:本文标题:javascript - Simulating a command queue and undo stack with RxJS - Stack Overflow 内容由网友自发贡献,该文观点仅代表作者本人, 转载请联系作者并注明出处:http://www.betaflare.com/web/1744966404a2634972.html, 本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容,一经查实,本站将立刻删除。
发表评论