1
0
mirror of https://github.com/Jermolene/TiddlyWiki5 synced 2024-11-27 03:57:21 +00:00

Extend refresh throttling to tiddlers having a "throttle.refresh" field

See discussion here: https://groups.google.com/d/msgid/tiddlywiki/7738644f-b53f-4fb0-b0df-16243fe51795%40googlegroups.com
This commit is contained in:
Jeremy Ruston 2019-10-14 10:42:14 +01:00
parent 03e98d1d62
commit 6089c4de29
10 changed files with 110 additions and 43 deletions

View File

@ -31,6 +31,7 @@ source: The source URL associated with a tiddler
subtitle: The subtitle text for a wizard subtitle: The subtitle text for a wizard
tags: A list of tags associated with a tiddler tags: A list of tags associated with a tiddler
text: The body text of a tiddler text: The body text of a tiddler
throttle.refresh: If present, throttles refreshes of this tiddler
title: The unique name of a tiddler title: The unique name of a tiddler
toc-link: Suppresses the tiddler's link in a Table of Contents tree if set to: ''no'' toc-link: Suppresses the tiddler's link in a Table of Contents tree if set to: ''no''
type: The content type of a tiddler type: The content type of a tiddler

View File

@ -25,7 +25,7 @@ var PAGE_TEMPLATE_TITLE = "$:/core/ui/PageTemplate";
// Time (in ms) that we defer refreshing changes to draft tiddlers // Time (in ms) that we defer refreshing changes to draft tiddlers
var DRAFT_TIDDLER_TIMEOUT_TITLE = "$:/config/Drafts/TypingTimeout"; var DRAFT_TIDDLER_TIMEOUT_TITLE = "$:/config/Drafts/TypingTimeout";
var DRAFT_TIDDLER_TIMEOUT = 400; var THROTTLE_REFRESH_TIMEOUT = 400;
exports.startup = function() { exports.startup = function() {
// Set up the title // Set up the title
@ -78,12 +78,12 @@ exports.startup = function() {
} }
// Add the change event handler // Add the change event handler
$tw.wiki.addEventListener("change",$tw.perf.report("mainRefresh",function(changes) { $tw.wiki.addEventListener("change",$tw.perf.report("mainRefresh",function(changes) {
// Check if only drafts have changed // Check if only tiddlers that are throttled have changed
var onlyDraftsHaveChanged = true; var onlyThrottledTiddlersHaveChanged = true;
for(var title in changes) { for(var title in changes) {
var tiddler = $tw.wiki.getTiddler(title); var tiddler = $tw.wiki.getTiddler(title);
if(!tiddler || !tiddler.hasField("draft.of")) { if(!tiddler || !(tiddler.hasField("draft.of") || tiddler.hasField("throttle.refresh"))) {
onlyDraftsHaveChanged = false; onlyThrottledTiddlersHaveChanged = false;
} }
} }
// Defer the change if only drafts have changed // Defer the change if only drafts have changed
@ -91,10 +91,10 @@ exports.startup = function() {
clearTimeout(timerId); clearTimeout(timerId);
} }
timerId = null; timerId = null;
if(onlyDraftsHaveChanged) { if(onlyThrottledTiddlersHaveChanged) {
var timeout = parseInt($tw.wiki.getTiddlerText(DRAFT_TIDDLER_TIMEOUT_TITLE,""),10); var timeout = parseInt($tw.wiki.getTiddlerText(DRAFT_TIDDLER_TIMEOUT_TITLE,""),10);
if(isNaN(timeout)) { if(isNaN(timeout)) {
timeout = DRAFT_TIDDLER_TIMEOUT; timeout = THROTTLE_REFRESH_TIMEOUT;
} }
timerId = setTimeout(refresh,timeout); timerId = setTimeout(refresh,timeout);
$tw.utils.extend(deferredChanges,changes); $tw.utils.extend(deferredChanges,changes);

View File

@ -1,5 +1,5 @@
created: 20130825213300000 created: 20130825213300000
modified: 20180104000000000 modified: 20191013093910961
tags: Concepts tags: Concepts
title: TiddlerFields title: TiddlerFields
type: text/vnd.tiddlywiki type: text/vnd.tiddlywiki
@ -9,43 +9,44 @@ type: text/vnd.tiddlywiki
The standard fields are: The standard fields are:
|!Field Name |!Reference |!Description | |!Field Name |!Description |
|`title` |TitleField |<<lingo title>> | |`title` |<<lingo title>> |
|`text` |TextField |<<lingo text>> | |`text` |<<lingo text>> |
|`modified` |ModifiedField |<<lingo modified>> | |`modified` |<<lingo modified>> |
|`modifier` |ModifierField |<<lingo modifier>> | |`modifier` |<<lingo modifier>> |
|`created` |CreatedField |<<lingo created>> | |`created` |<<lingo created>> |
|`creator` |CreatorField |<<lingo creator>> | |`creator` |<<lingo creator>> |
|`tags` |TagsField |<<lingo tags>> | |`tags` |<<lingo tags>> |
|`type` |TypeField |<<lingo type>> | |`type` |<<lingo type>> |
|`list` |ListField |<<lingo list>> | |`list` |<<lingo list>> -- see ListField |
|`caption` |CaptionField |<<lingo caption>> | |`caption` |<<lingo caption>> |
Other fields used by the core are: Other fields used by the core are:
|!Field Name |!Reference |!Description | |!Field Name |!Description |
|`class` |ClassField |<<lingo class>> | |`class` |<<lingo class>> |
|`color` |ColorField |<<lingo color>> | |`color` |<<lingo color>> |
|`description` |DescriptionField |<<lingo description>> | |`description` |<<lingo description>> |
|`draft.of` |DraftOfField |<<lingo draft.of>> | |`draft.of` |<<lingo draft.of>> |
|`draft.title` |DraftTitleField |<<lingo draft.title>> | |`draft.title` |<<lingo draft.title>> |
|`footer` |FooterField |<<lingo footer>> | |`footer` |<<lingo footer>> |
|`hide-body`|HideBodyField|<<lingo hide-body>>| |`hide-body`|<<lingo hide-body>>|
|`icon` |IconField |<<lingo icon>> | |`icon` |<<lingo icon>> |
|`library` |LibraryField |<<lingo library>> | |`library` |<<lingo library>> |
|`list-after` |ListAfterField |<<lingo list-after>> | |`list-after` |<<lingo list-after>> |
|`list-before` |ListBeforeField |<<lingo list-before>> | |`list-before` |<<lingo list-before>> |
|`name` |NameField |<<lingo name>> | |`name` |<<lingo name>> |
|`plugin-priority` |PluginPriorityField |<<lingo plugin-priority>> | |`plugin-priority` |<<lingo plugin-priority>> |
|`plugin-type` |PluginTypeField |<<lingo plugin-type>> | |`plugin-type` |<<lingo plugin-type>> |
|`source` |SourceField |<<lingo source>> | |`source` |<<lingo source>> |
|`subtitle` |SubtitleField |<<lingo subtitle>> | |`subtitle` |<<lingo subtitle>> |
|`toc-link`|TocLink|<<lingo toc-link>>| |`throttle.refresh` |<<lingo throttle.refresh>> |
|`toc-link`|<<lingo toc-link>>|
The TiddlyWebAdaptor uses a few more fields: The TiddlyWebAdaptor uses a few more fields:
|!Field Name |!Reference |!Description | |!Field Name |!Description |
|`bag` |BagField |<<lingo bag>> | |`bag` |<<lingo bag>> |
|`revision` |RevisionField |<<lingo revision>> | |`revision` |<<lingo revision>> |
Details of the fields used in this ~TiddlyWiki are shown in the [[control panel|$:/ControlPanel]] {{$:/core/ui/Buttons/control-panel}} under the <<.controlpanel-tab Info>> tab >> <<.info-tab Advanced>> sub-tab >> Tiddler Fields Details of the fields used in this ~TiddlyWiki are shown in the [[control panel|$:/ControlPanel]] {{$:/core/ui/Buttons/control-panel}} under the <<.controlpanel-tab Info>> tab >> <<.info-tab Advanced>> sub-tab >> Tiddler Fields

View File

@ -1,10 +1,12 @@
created: 20150619162409306 created: 20150619162409306
modified: 20150619162511957 modified: 20191014091803518
tags: [[Hidden Settings]] tags: [[Hidden Settings]]
title: Hidden Setting: Typing Refresh Delay title: Hidden Setting: Typing Refresh Delay
type: text/vnd.tiddlywiki type: text/vnd.tiddlywiki
TiddlyWiki defers processing changes to draft tiddlers until a timeout has elapsed. The default value of 400ms gives a good balance of responsiveness in most cases but isn't always optimal on lower powered mobile devices. TiddlyWiki defers processing changes to draft tiddlers until a timeout has elapsed (this is called throttling). The mechanism can be extended to other tiddlers by adding a `throttle.refresh` field. See RefreshThrottling for details.
The default value of 400ms gives a good balance of responsiveness in most cases but isn't always optimal on lower powered mobile devices.
The timeout can now be changed by changing this value (in milliseconds): The timeout can now be changed by changing this value (in milliseconds):

View File

@ -0,0 +1,13 @@
created: 20191012152414236
modified: 20191014091753894
tags: Mechanisms
title: RefreshMechanism
type: text/vnd.tiddlywiki
The refresh mechanism is the part of the WikificationMechanism concerned with updating a rendering when there are changes in the tiddler store.
The refresh mechanism is notified of changes to the tiddler store asynchronously. This is done so that multiple consecutive changes can be coalesced into a single change notification. Thus, a series of action widgets modifying several different tiddlers will only trigger a single refresh cycle.
When changes occur, the rendering is updated by calling the "refresh" method of the root widget. The refresh method determines whether the widget needs to be updated to reflect the incoming changes, and then recursively calls into the refresh methods of each child widget
The refresh cycle is inherently fairly slow because it involves visiting every node in the render tree. To maintain performance there is a RefreshThrottling mechanism that enables refresh processing to be deferred when rapid changes occur to the same tiddler.

View File

@ -0,0 +1,16 @@
created: 20191013095916159
modified: 20191014093837558
tags: RefreshMechanism
title: RefreshThrottling
type: text/vnd.tiddlywiki
The RefreshMechanism allows the refresh cycle to be throttled (or deferred) when rapid changes occur to the same tiddler. It is used to maintain responsiveness while editing a draft tiddler, but can also be used on other tiddlers.
The rules governing refresh throttling are:
* When a change notification occurs, throttling will only take place if all of the modified tiddlers meet at least one of these criteria:
** Has the field `draft.of`
** Has the field `throttle.refresh`
* If the refresh cycle is to be throttled, a timer is set for the internal specified in [[$:/config/Drafts/TypingTimeout|Hidden Setting: Typing Refresh Delay]] (cancelling any preciously set timer)
** When the timer fires, the refresh cycle is triggered, passing the aggregated titles of all the deferred refresh cycles

View File

@ -0,0 +1,28 @@
created: 20191012080221911
modified: 20191013094002890
tags: Mechanisms
title: WikificationMechanism
type: text/vnd.tiddlywiki
"Wikification" is a general term for the dynamic process of converting tiddlers containing WikiText into the HTML DOM representation needed by the browser, and updating that representation if the underlying tiddlers change.
It is composed of several distinct steps:
* ParserMechanism: reading the text of tiddlers and scanning for wikitext constructions, outputting a tree representation of the resulting structure. It is an expensive process so parse trees are cached, and only need to be updated if the corresponding tiddler is changed
* WidgetMechanism: starting with a specified root tiddler, recursively instantiate a widget for each parse tree node making a rendering tree. Widgets can optionally also create DOM nodes
* RefreshMechanism: handling changes to the tiddler store by selectively and efficiently updating a rendering tree
This mechanism is used in the browser to build TiddlyWiki's main interactive page. At startup, the tiddler $:/core/ui/PageTemplate is parsed and rendered to the DOM, recursively pulling in other tiddlers to build the entire user interface. Any user interactions -- following a link, clicking a button, or typing in a text box -- trigger a change in the tiddler store which then automatically propagates through the widget tree. For example, if the user clicks a link to navigate to a new tiddler, the following steps take place:
# Clicking the link triggers the action of the LinkWidget which by default is to add the target tiddler to the list field of the tiddler $:/StoryList
# The modification to the tiddler store asynchronously triggers the refresh cycle. The asynchronous triggering ensures that the refresh cycle is only run once even if multiple tiddlers were modified in succession
# The refresh cycle recursively visits each node of the render tree giving them the chance to update themselves in the light of the accumulated changes to the tiddler store. In this case, the ListWidget of the main story river notices that a single tiddler needs to be added to the river, and renders that newly displayed tiddler without disturbing the other tiddlers
The performance of the entire wikification process is critical. If the refresh cycle takes more than about 400ms then the user will notice a delay between their actions and the effects. See [[Performance]] for some discussion of how to optimise performance.
The rendering process is also aggressively reused in other parts of TiddlyWiki, both in the browser and on the server:
* Generating TiddlyWiki's standalone HTML representation
* Creating static HTML renderings of tiddlers
* Dynamically rendering CSS stylesheet tiddlers

View File

@ -1,15 +1,20 @@
created: 20150330155120127 created: 20150330155120127
modified: 20190609154450433 modified: 20191014091943444
tags: [[Working with TiddlyWiki]] tags: [[Working with TiddlyWiki]]
title: Performance title: Performance
type: text/vnd.tiddlywiki type: text/vnd.tiddlywiki
TiddlyWiki ships with defaults that are designed to get the best out of modern devices from smartphones to desktop computers. If you need to work on older, less powerful devices, or work with large amounts of content, there are a few steps you can take to improve performance. TiddlyWiki ships with defaults that are designed to get the best out of modern devices from smartphones to desktop computers. If you need to work on older, less powerful devices, or work with large amounts of content, there are a few steps you can take to improve performance.
!! Usage
* ''Avoid the "Recent" tab''. It is computationally slow to generate and update in response to tiddler changes. * ''Avoid the "Recent" tab''. It is computationally slow to generate and update in response to tiddler changes.
* ''Use the "Vanilla" theme''. The default "Snow White" theme includes visual effects like shadows, transparency and blurring that can be slow to render on older devices * ''Use the "Vanilla" theme''. The default "Snow White" theme includes visual effects like shadows, transparency and blurring that can be slow to render on older devices
* ''Avoid large tiddlers''. Large bitmaps can significantly slow TiddlyWiki's performance. For example, an image taken with a modern smartphone will often be 5MB or more. Use ExternalImages whenever possible * ''Avoid large tiddlers''. Large bitmaps can significantly slow TiddlyWiki's performance. For example, an image taken with a modern smartphone will often be 5MB or more. Use ExternalImages whenever possible
* ''Don't have too many tiddlers open at once''. Every tiddler you have open will require processing to keep it up to date as the store changes (for example, while you type into a draft tiddler). It is particularly easy when using zoomin story view to end up with dozens of tiddlers listed in the ''Open'' tab in the sidebar. Get into the habit of periodically closing all open tiddlers with the <<.icon $:/core/images/close-all-button>> ''close all'' button * ''Don't have too many tiddlers open at once''. Every tiddler you have open will require processing to keep it up to date as the store changes (for example, while you type into a draft tiddler). It is particularly easy when using zoomin story view to end up with dozens of tiddlers listed in the ''Open'' tab in the sidebar. Get into the habit of periodically closing all open tiddlers with the <<.icon $:/core/images/close-all-button>> ''close all'' button
!! WikiText
* ''Use the built-in performance instrumentation''. Studying the [[performance instrumentation|Performance Instrumentation]] results can help highlight performance problems * ''Use the built-in performance instrumentation''. Studying the [[performance instrumentation|Performance Instrumentation]] results can help highlight performance problems
* Take advantage of indexed filter operators. The following constructions at the start of a filter run will be optimised to run many times faster than otherwise: * Take advantage of indexed filter operators. The following constructions at the start of a filter run will be optimised to run many times faster than otherwise:
** `[all[tiddlers]tag[x]...` ** `[all[tiddlers]tag[x]...`
@ -22,4 +27,5 @@ TiddlyWiki ships with defaults that are designed to get the best out of modern d
** `[all[shadows+tiddlers]field:y[x]...` ** `[all[shadows+tiddlers]field:y[x]...`
** Note that the field indexer currently defaults to indexing field values of less than 128 characters; longer values can still be searched for, but no index will be constructed ** Note that the field indexer currently defaults to indexing field values of less than 128 characters; longer values can still be searched for, but no index will be constructed
** Also note that the “field” operator is also used when the operator name is a fieldname, so, for example, `[all[shadows+tiddlers]caption[x]...` is optimised. ** Also note that the “field” operator is also used when the operator name is a fieldname, so, for example, `[all[shadows+tiddlers]caption[x]...` is optimised.
* Use the [[throttling|RefreshThrottling]] feature of the RefreshMechanism judiciously