-
Notifications
You must be signed in to change notification settings - Fork 650
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
tag to handle streams #313
Comments
Example time: Let's say we want to create a list that has a few users in it and a button to load more users. Those users come from some service call. With <await(users from data.usersProvider) client-reorder>
<await-placeholder>
<card>
<loading-indicator/>
</card>
</await-placeholder>
<for(user in users)>
<card>
<img src=user.avatar />
<info>
<name>${user.name}</name>
<handle>${user.handle}</handle>
</info>
</card>
</for>
<button>load more</button>
</await> In this case, the placeholder card would spin until the data gets back from the service call, then we render the user cards and load more button and replace the placeholder with these. With <on(user in data.usersStream) client-reorder>
<on-placeholder>
<card>
<loading-indicator/>
</card>
</on-placeholder>
<card>
<img src=user.avatar />
<info>
<name>${user.name}</name>
<handle>${user.handle}</handle>
</info>
</card>
<on-finish>
<button>load more</button>
</on-finish>
</on> In this case, the placeholder would spin until all user cards have loaded. As each user is emitted by the stream, a user card would be rendered and inserted right before the loading card. Once all user cards were loaded, the load more button would be displayed and the placeholder removed. Here's a mockup of the difference. |
More thoughts: It might be useful be able to see what iteration you're on like the
for(var i = 0; i < array.length; i++) {
console.log(array[i]);
}
assert(i === array.length); This could be used to:
<on(user in data.usersStream | status-var=loop)>
<on-timeout>
<if(loop.getIndex() > 0)>
We timed out, but at least we got some data!
</if>
<else>
We timed out and got nothing...
</else>
</on-timeout>
${user.name}
</on>
|
Good idea! I need it. |
This sounds great! We would be able to use this right away. We're basically doing this manually in a renderer.js file ourselves now, but this is much cleaner and doesn't require us to expose all the Are there any strong feelings in making this a separate tag? Would it make sense to just have |
@scttdavs The main difference, is in passing a stream to I actually just threw something together this afternoon because another team at eBay was asking about this. It doesn't have any of the timeout/placeholder/buffering/flushing/etc. in place, but it's a good starting point. I probably won't have time to revisit this for a little while, so if you're interested in working on this, that would be awesome! /cc @RajaRamu (on the aforementioned team at eBay) |
If there's enough interest, it would be fairly simple for Alternatively, most streams can simply be converted to Promises with something like new Promise(function(resolve, reject) {
var buffer = [];
stream.on('error', reject);
stream.on('data', function(data) {
buffer.push(data);
});
stream.on('end', function() {
var result = Buffer.concat(buffer).toString();
resolve(result);
});
}); Hopefully this weekend I'll get the chance to play with the |
I think there's enough differences between handling a |
@patrick-steele-idem If Though here's another thought that just popped to mind: What about something like that for the
...as an alternate syntax to allow binding to events other than just 'data'? |
On second thought, something like this might be better instead:
('from' and 'of' are just what came off the top of my head, it may need re-wording) |
I kind of like that implementation too. One concern I would have is that there's no clear way to use I think |
(Just noticed the original post had an example of using a generic EventEmitter which accomplishes basically the same thing as my previous post, not sure how I missed that a few days ago.) Hmm... Rather than starting with standard streams and adding different syntaxes to support non-standard use cases, what if we looked at this the other way around? We could start by exposing the generic behavior:
Then add convenience syntaxes for the most common uses:
Note that |
I'll throw out another proposal: <ul>
<await(data.usersStream)>
<on('data' as user)>
<li>${user.name}</li>
</on>
</await>
</ul> |
@patrick-steele-idem - this sounds good to me. Looks lot cleaner! |
@mlrawlings @philidem @tindli @austinkelleher @Hesulan @scttdavs should we go forward with using |
@patrick-steele-idem @mlrawlings - will accept event emitter going forward. right now, its not supporting event emitter. i think its essential to support that with respect to streaming |
I can't think of any reason not to. Just one question: If the object has both Also, a few more ideas:
|
@patrick-steele-idem - I like that syntax a lot. |
@patrick-steele-idem I like the new proposal, especially reusing with <ul>
<await(data.usersStream)>
<@on('data' as user)>
<li>${user.name}</li>
</@on>
// how is content here handled?
</await>
</ul> I also had a question in the comment above^ |
There's the potential argument against
{
on:[{
event:'data',
renderBody:function(out, user) {
out.w('<li>'+escapeXML(user.name)+'</li>';
}
}]
} @scttdavs The rules around content outside In the case of the |
@mlrawlings @austinkelleher @patrick-steele-idem any tips and tricks for applying infinite scrolling with marko? |
FYI this can be done in userland. Although maybe it eventually becomes a core tag: // usage
<for-await|item| of=asyncIterable>
</for-await>
// impl
$ const iterable = input.of[Symbol.asyncIterator]();
<macro name="next">
<await(iterable.next())>
<@then|item|>
<${input.renderBody}(item.value)/>
<if(!item.done)>
<next/>
</if>
</>
</>
</macro>
<next/> |
@patrick-steele-idem and I have discussed adding a tag to marko's core that handles streaming in an elegant way.
The syntax that we're looking at is the
<on>
tag:We'd also support a more verbose syntax for generic EventEmitters:
In many ways, the
<on>
tag will be similar to the<await>
tag (previously<async-fragment>
), however the<on>
tag will also flush multiple times throughout it's rendering cycle.There is a tradeoff to consider around flushing. If we flush after each iteration, content will be pushed to the browser sooner, but if we buffer a few iterations and gzip is enabled, the resulting payload will be smaller. We need a good default for flushing and an easy, intuitive way to configure it.
We also need to figure out what should be provided by the tag, and what interesting use cases we want to support. One that comes to mind immediately is infinite scrolling.
All that said, here is my proposed set of attributes for the
<on>
tag:@var
- parsed from the argument, the variable to be used inside the body of the tag@event
- parsed from the argument, the name of the event to listen to, defaults to "data"@endEvent
- parsed from the argument, the name of the event that signifies the end of the loop, defaults to "end"@data-provider
- parsed from the argument, the stream or emitter we're listening to@name
- the name of the<on>
tag. used for debugging and theshow-after
attribute@client-reorder
- the<on>
tag will not block the rest of the page, JS is required on client@show-after
- whenclient-reorder
is set to true then displaying this instance's content will be delayed until the referenced<await>
or<on>
instance is shown.@buffer-count
- how many iterations of the<on>
tag to buffer before flushing@buffer-duration
- if the time since the last flush is greater than the buffer duration, then the<on>
tag will flush its buffer, regardless of the current buffer count@timeout
- if the time since the last chunk of data was emitted is greater than the timeout, then the<on>
tag will timeout@total-timeout
- if the time since the rendering started is greater than the total timeout, then the<on>
tag will timeout@error-message
- a message that is shown if the stream emits an error@timeout-message
- a message that is shown if the stream times out@placeholder-message
- a message that is shown while the<on>
tag is streaming@empty-message
- a message that is shown if the stream ends without emitting data@finish-message
- a message that is shown when/if the stream completes successfullyAlso, we'll provide
<on-placeholder>
,<on-error>
,<on-timeout>
,<on-empty>
, and<on-finish>
as alternatives*-message
attributes.The text was updated successfully, but these errors were encountered: