perf(autolinker): Use requestIdleCallback to fix hanging on large bodies

This commit is contained in:
Ben Gotow 2016-03-23 19:02:51 -07:00
parent 75c9b116bb
commit 75ff8282a8
4 changed files with 28 additions and 8 deletions

View file

@ -28,7 +28,7 @@ function _runOnTextNode(node, matchers) {
}
}
export function autolink(doc) {
export function autolink(doc, {async} = {}) {
// Traverse the new DOM tree and make things that look like links clickable,
// and ensure anything with an href has a title attribute.
const textWalker = document.createTreeWalker(doc.body, NodeFilter.SHOW_TEXT);
@ -38,8 +38,21 @@ export function autolink(doc) {
['', RegExpUtils.urlRegex({matchEntireString: false})],
];
while (textWalker.nextNode()) {
_runOnTextNode(textWalker.currentNode, matchers);
if (async) {
const fn = (deadline) => {
while (textWalker.nextNode()) {
_runOnTextNode(textWalker.currentNode, matchers);
if (deadline.timeRemaining() <= 0) {
window.requestIdleCallback(fn, {timeout: 500});
return;
}
}
};
window.requestIdleCallback(fn, {timeout: 500});
} else {
while (textWalker.nextNode()) {
_runOnTextNode(textWalker.currentNode, matchers);
}
}
// Traverse the new DOM tree and make sure everything with an href has a title.

View file

@ -65,7 +65,7 @@ export default class EmailFrame extends React.Component {
doc.write(`<div id='inbox-html-wrapper'>${this._emailContent()}</div>`);
doc.close();
autolink(doc);
autolink(doc, {async: true});
// Notify the EventedIFrame that we've replaced it's document (with `open`)
// so it can attach event listeners again.

View file

@ -55,7 +55,7 @@ describe "AccountStore", ->
(new Account).fromJSON(@configAccounts[1])
])
it "should initialize tokens from config, if present, save them to keytar, and remove them from config", ->
it "should initialize tokens from config, if present, and save them to keytar", ->
@configTokens = {'A': 'A-TOKEN'}
@instance = new @constructor
expect(@instance.tokenForAccountId('A')).toEqual('A-TOKEN')

View file

@ -35,6 +35,9 @@ class MessageBodyProcessor {
return;
}
// grab the old value
const oldOutput = this._recentlyProcessedD[changedKey].body;
// remove the message from the cache
delete this._recentlyProcessedD[changedKey];
this._recentlyProcessedA = this._recentlyProcessedA.filter(({key}) =>
@ -52,9 +55,13 @@ class MessageBodyProcessor {
const updatedMessage = changedMessage.clone();
updatedMessage.body = updatedMessage.body || subscriptions[0].message.body;
const output = this.retrieve(updatedMessage);
for (const subscription of subscriptions) {
subscription.callback(output);
subscription.message = updatedMessage;
// only trigger if the output has really changed
if (output !== oldOutput) {
for (const subscription of subscriptions) {
subscription.callback(output);
subscription.message = updatedMessage;
}
}
}
}