PDA

View Full Version : [INFOREQ] Router - multi-token routes should execute in order



LesJ
5 Jun 2017, 5:37 AM
According to the documentation multiple token routes execute in order, but they don't.

They only begin execution in order, but then the order of route execution can be unpredictable.

This happens because an asynchronous code may be run before a route is entered, which is a typical use case.

This is at the minimum a documentation issue, but it really should be fixed so multiple route execution is always in order when preceded by asynchronous code.

See also my question (https://www.sencha.com/forum/showthread.php?346700-Router-using-multiple-routes)where I provided a fiddle showing the issue.

LesJ
23 Jun 2017, 5:32 AM
*** bump - almost 3 weeks passed

LesJ
7 Aug 2017, 10:52 AM
I posted this bug over two months ago and there's still no response. I'm not sure if this an actual bug or a documentation issue.

Here's a fragment from the router doc: Using Multiple Routes in a Single Hash (https://docs.sencha.com/extjs/6.5.1/guides/application_architecture/router.html#application_architecture-_-router_-_using_multiple_routes_in_a_single_hash)

"It's important to note that the execution order of the individual routes is the same order as the routes are in the hash. The user/1234 route will always execute before the messages route for the above example."

The router code makes a massive and unwarranted assumption that no asynchronous code will run before a route is entered.

However, if asynchronous code is executed before a route is entered (which is very likely), there's no guarantee that individual routes will execute in order.

evant
7 Aug 2017, 3:07 PM
I'm not entirely clear on why you think this is a bug. You've said you think it's a bug, but you don't really explain why. In this case you're writing code to delay the execution of said functions. For example, given the following code:



// The requests will execute in order
Ext.Ajax.request({
url: 'foo',
success: function() {}
});
Ext.Ajax.request({
url: 'bar',
success: function() {}
});


If "bar" takes 1s to complete and "foo" takes 5s, obviously the callbacks will run "out of order". But the requests themselves are executed in order.

LesJ
7 Aug 2017, 3:25 PM
The routes may execute not in order if the route "before" code is async, which is very likely.

I provided a fiddle in the linked question. You will see that the route "B" completes before route "A", i.e. not in order.


MyRouter.redirectTo('routeA|routeB');

56742

Clearly, this looks like a bug to me since a documented router feature is not working as advertised.

evant
7 Aug 2017, 3:45 PM
I understand what you're saying, but I'm not sure was ever really the intent. Certainly the entry points to the routes are executed in order and excluding outside intervention that also holds true.

Can you explain why this is problematic?

LesJ
7 Aug 2017, 4:17 PM
Only the route "before" code starts executing in order, but the routes may not execute in order as demonstrated in my fiddle demo.

It's a problem because the code doesn't work as documented. It makes an assumption that the "before" code is synchronous, which typically won't be the case. The Ext JS doc states that "the execution order of the individual routes is the same order as the routes are in the hash". This may or may not be the case.

I already converted a PathJS-based router where the original developer ensured that routes execute in order even if the "before" code is async. It would take me too much time to test would happen if the routes don't execute in order. It's possible that another Ext JS developer will face a similar problem.

Perhaps this is just a documentation issue, but to me this was a practical problem which I had to solve when using the Ext JS router.

Basically, the Ext JS router should try to synchronize route execution when there's async route "before" code, but it doesn't do that.

LesJ
7 Aug 2017, 4:41 PM
Can you explain why this is problematic?

Multi-token routing including async "before" code might be a rarely used feature, so I'd say let's wait and see if other developers consider this an issue.

evant
8 Aug 2017, 3:05 AM
It's not really my area of expertise, I can see what you're saying but not necessarily sure if it's a problem or not. It could swing both ways IMO, but I guess if we take the example in the docs:

"/user/1234|messages", depending on your app it may be possible for them to execute asynchronously, as long as you enter each route (via befores) first. I can also see wanting to wait for the user to complete entirely before moving onto messages at all.

For example, you could feasibly have:

1)
-beforeUser
-beforeMessage
-message
-user

2)
-beforeUser
-beforeMessage
-user
-message

mitchellsimoens
8 Aug 2017, 4:49 AM
Individual tokens in the hash are split and executed in a sandboxed manner from each other so when you mix in async before, the execution will not be guaranteed. In the "user/1234|messages" route, you may need to check if the "user/1234" route is allowed for security reasons so the before action should contact the server but all users have access to the "messages" route. So the "messages" route isn't dependent on the "user/1234" route. Of course this may not be exactly what your application wants, maybe your application wants the "messages" route to be delayed until the "user/1234" is completed, so either way can be desired but the framework isn't going to know this dependency as the "messages" route may depend on none or multiple other routes and there is no connecting that dependency chain currently. Also, when using the redirectTo method and passing an object to add/update/remove tokens to the hash, the order of the tokens in the has has no guarantee. Routes don't have an order or priority config currently.

One way you could currently do it (in 6.5) is you can suspend/resume the router at will so in your before action. You could also architect some means of your messages route waiting for that user route to finish. The router even fires global events on executions now that you can listen to in order to continue the messages. So there are ways around it but nothing built in at the moment.

Since routes are named now, we could add in a depends config like we do with Ext.data.field.Field. Since routes get added/removed now with ViewController's lifecycle the code would have to make those dependencies optional if the route isn't around and I'm not sure how much work that would be and am not promising anything here.

I use multiple tokens in fiddle and portal heavily and I have generic methods to ensure certain things are present so my routes aren't dependent on each other but are dependent on application state. So if I had a messages route that depended on some components that the user route would create, the messages route would tell the application "I need x, y, z present" and if they aren't then it'd create it. So if the user route would finish after the messages route, it would tell the application "I need x, y, z present" and since the messages route already told the application about that need the application wouldn't create those components and would then carry on. So I took a different route than you have where my routes don't know about each other, they just know about the application state they care about.

LesJ
8 Aug 2017, 5:45 AM
Multi-token routing is extensively documented and it clearly states that route execution is in order. However, there's a major assumption that the code executing before a route is entered is synchronous. If the code is async (which is a typical use case), route execution order is not guaranteed. This major gotcha is not mentioned in the doc. Definitely the doc is out of whack with the way multi-token routing actually works in Ext JS.

Here's an example why route execution should be in order even if each route is preceded by async code.

Multi-token routing can be useful for instance to open multiple tabs at ones while preserving navigation history. Before each tab is added, the routing code will usually make an async request to the server to see if the user is authorized to access the code, get info about the tab, etc. Of course, tabs must be added in order; otherwise, the UI will not look consistent.

mitchellsimoens
8 Aug 2017, 5:54 AM
Yes, as both Evan and I have said we see the need for both ways. As I have said, I don't want my routes to wait on others and I have even talked about what could possibly be done to achieve both ways.

I think you are inferring something different from the docs than I am. The tokens are executed in order but as soon as you introduce something async, the order is then mucked with just like JavaScript works. For example:

24p8

The methods are executed but since the bar uses a setTimeout, it's message will be out of order of when the method was executed. This is very similar to the issue you have with the router.

So like I said in my previous post, I can see the need for routes to depend on one-another but I can also see not wanting them to. I believe there is ways around it currently (especially since you can even add callbacks onto the action that is passed to a before) and ways we can make this configurable in the future.

I see this as a feature request more than a bug and I do not see the docs saying something that is untrue. The docs could say something more that the final execution order is dependent on any async befores but things are kicked off in the order they are in the hash.

LesJ
8 Aug 2017, 6:07 AM
I see this as a feature request more than a bug and I do not see the docs saying something that is untrue. The docs could say something more that the final execution order is dependent on any async befores but things are kicked off in the order they are in the hash.

Right, the doc should say that route execution order depends on the async "before" code. I'm pretty sure this point is more important than how route execution begins.

Currently the doc states: "Each route will execute in the order dictated by the hash.", but this applies only to how the route execution begins, and the actual routes may or may not be executed in order.

This is a major gotcha that needs to be documented.



So like I said in my previous post, I can see the need for routes to depend on one-another but I can also see not wanting them to


If we can have options how multi-token routes can execute, this would be great.