Applications behave consistently because all state is stored in a single object, so it can't be desynchronized across components. Because all updates use a single function, it becomes easy to track and respond to any state changes by applying middlewares to that function. Things are more testable because all state is available for stubbing, and because the "reducer" is a testable, pure function typically composed of many other smaller, testable pure functions.
Things like time travel become possible because of the purity of the reducing function. Since it's pure, it can't modify the old state object (technically, it can, but it's against the rules). So in theory, we can undo any state change simply by loading the old state object from where it's being stored by some kind of history middleware.
The cons (you may/may not see them as such, but..) are
a. for a lot of people it is quite difficult to grok at first, b. it's extremely functional, c. it has quite a lot of boilerplate - it is extremely explicit, d. the app state is held in a single immutable object.
It also defines certain conventions around updating that data. In Backbone, anyone with a reference to a Model can call myModel.set("someField", someValue) at any time. In Redux, "write" logic is isolated to a user-defined set of "reducer functions", which only respond to specific types of messages. This makes it _very_ easy to determine when and why a particular bit of data changed. In addition, it enables scenarios such as storing the entire list of dispatched actions, and then playing back each action step-by-step to allow you to see how you got to a given state in your application (aka "time-travel debugging").