There's a subtlety in using Promises with event handlers or any code that's executed asynchronously.
Promises are supposed to be used for future values, with the reject
handler acting like an exception handler, so that the Promise constructor actually catches real exceptions and turns them into reject
s (from the polyfill; I assume the browser-implemented code works similarly):
try {
resolver(resolvePromise, rejectPromise);
} catch(e) {
rejectPromise(e);
}
But asynchronous code runs outside the execution unit that runs resolver()
, so any exceptions there have to be caught explicitly:
<button id=one>Click Me</button>
<button id=two>Don't Click Me</button>
<script>
var promise = new Promise(function(resolve, reject){
document.querySelector('#one').onclick = function(){
resolve ('You Clicked!');
};
document.querySelector('#two').onclick = function(){
throw new Error ('You Clicked!');
};
});
promise.then(function(msg){
alert('Button clicked: '+msg);
}, function(err){
alert('Error caught:'+err.message);
});
</script>
Clicking button two gives an uncaught exception error, not the alert from the then()
. You have to catch the exceptions:
document.querySelector('#two').onclick = function(){
try {
throw new Error ('You Clicked!');
} catch (e) {
reject(e);
}
};
or resolve to a new promise that can catch the error:
document.querySelector('#two').onclick = function(){
resolve(new Promise(function (){
throw new Error ('You Clicked!');
}));
};
The other subtlety (which is obvious from the definition of a Promise) is that Promises are once-only; in the above code, clicking either button settles the Promise and further clicking does nothing. Event handlers can be called multiple times, so you have to think about what you intend.
Posted by Danny on December 18, 2013 at 9:39 am under Javascript.
Comment on this post.
Just in time for me to start thinking about Javascript Promises, comes along an official API, along with a polyfill that basically re-implements rsvp.js with the "official" terminology.
Now for some experimenting (and rewriting jquery.status.js
to use real Promises).
It seems from all the discussion that jQuery $.Deferred
are not and will not be real Promises, but they can be Promise.cast($.Deferred())
into one. Note that to use that, you need to $.Deferred().resolve()
on the jQuery object, but attach Promise.then()
's to the Promise.
Posted by Danny on December 17, 2013 at 1:37 pm under Javascript.
1 Comment.
Prism is a great Javascript syntax highlighter, and it's fast enough to be used on the fly for active editing; Lea Verou (the author) does this with
Dabblet. Combined with
bililiteRange
to handle some of the subtleties of dealing with
contenteditable
elements, a syntax-highlighting editor is relatively easy.
Simplest usage: bililiteRange.fancyText(element, Prism.highlightelement);
.
See the demo.
See the code on github.
There's a fair amount of flakiness; contenteditable
elements are still handled inconsistently. Newlines are changed into actual elements (Firefox adds <br>
s; Chrome adds a whole new <div>
), even though a <pre>
doesn't need that; it would interpret newlines the way we want. So we have to intercept those and just insert the newline. Firefox insists on changing newlines into <br>
's even in pasted text, and Chrome's clipboard implementation includes the carriage return character, so we have to intercept paste events and insert the text appropriately mangled.
Continue reading ‘Simple syntax-highlighting editor with Prism’ »
Posted by Danny on December 16, 2013 at 4:01 pm under bililiteRange, Javascript.
4 Comments.
I've been looking at Promise/A+ as a way to abstract future values, or values that depend on something the user will do later, specifically Yehuda Katz's rsvp.js
, and came across Domenic Denicola's You're Missing the Point of Promises, which got me looking at Oliver Steele's Minimizing Code Paths in Asychronous Code. Now I have another consideration in my code that I never thought about before.
Continue reading ‘Thinking about synchonicity’ »
Posted by Danny on December 16, 2013 at 3:04 pm under Javascript.
3 Comments.
This plugin is obsolete. See the new status
at github.bililite.com/status.
Updated 2015-01-19 to allow $(console).status()
. Now at version 1.4.
Updated 2013-12-18 to use Promise
s. This necessitated removing the result
option and adding the returnPromise
.
Updated 2014-01-09 to add initialText
option and to document the markup used.
For the Kavanot editor I wanted to have a way to display messages (like "Text saved successfully") that would show up on screen then fade out, and a way to enter text (like the title, or editing commands if I ever get that working). I packaged that all together into a plugin could handle both, with an anticipated use like:
<textarea id=editor></textarea>
<div id=notifications></div>
And then do something like $('#notifications').status('Text saved');
and that briefly shows the message in the <div>
and then gradually fade out.
And for user input,
$('#notifications').status({
prompt: 'File Name:',
run: function (text) {
/* do something with text */
if (successful) return 'success message';
if (! successful) throw {message: 'failure message'};
}
});
displays the prompt and an input box then runs the function and displays the result (it automatically catches
the throw
n exception).
See the code on github.
See a demo.
Continue reading ‘New jQuery plugin, status
’ »
Posted by Danny on December 12, 2013 at 10:15 pm under jQuery.
1 Comment.
Looking back at my line numbering plugin for Prism, I realized that it's working too hard by manipulating the DOM. Prism works by string manipulation anyway, so there's no harm in using that to wrap the lines rather than searching through elements. The code is now only 6 lines long. It still uses CSS and ::before
pseudoelements to show the line numbers.
Posted by Danny on December 9, 2013 at 10:53 am under Javascript, Web Design.
Comment on this post.
Just upgraded the server to Apache 2.4, and started getting 500 errors on everything. Turns out the access control was completely changed; instead of Order deny,allow
and
Deny from all
you use Require all denied
. That change fixed everything. Hope this helps someone.
Posted by Danny on October 22, 2013 at 2:35 pm under Uncategorized.
Comment on this post.
The Torah as traditionally written has two kinds of paragraph breaks: "open" and "closed". Open paragraphs are the same as in English typography: the next paragraph starts on the next line. Closed paragraphs start the next paragraph on the same line, after a 9-em gap. In the image on the page linked to above, there are 3 closed paragraph breaks, 2 open, then 1 closed.
I was thinking about representing this in CSS, with semantic HTML; something like <p class=open>First Paragraph</p><p class=closed>Second paragraph</p>
. The Mechon Mamre tikkun uses a single <p>
for the entire portion, then has <br>
's and
's hard-coded into the text. But there ought to be a better way.
See the final formatting.
Continue reading ‘Formatting the Torah’ »
Posted by Danny on October 22, 2013 at 12:03 pm under Judaism, Web Design.
1 Comment.
I never really liked how my hotkeys plugin (that allows things like $('body').on('keydown', {keys: '^A'}, function() {alert ('Control-A!'); })
) worked internally, and it didn't allow multiple handlers on a single element for a given key combination, and required initialization. I went back and looked at it some more, and studied John Resig's hotkeys, and came up with a simpler, better version. Use is largely the same, but there's no $.keymap.hotkeys('keydown')
to initialize, and you have to explicitly remove handlers with $().off()
rather than replacing with a new handler.
See the code.
Posted by Danny on September 17, 2013 at 2:59 pm under jQuery.
Comment on this post.
This is obsolete. The documentation is now on github.bililite.com/timerevents.
Code and demo are on github.
I wanted to have a simple word count on the Kavanot editor, and this:
$('span.wordcount').text($('#editor').val().split(/\s+/).length+' words');
is enough for me. Keeping it live is straightforward:
$('#editor').on('input', function(){
$('span.wordcount').text(this.value.split(/\s+/).length+' words');
});
But that won't show the word count when the page is first loaded. The ready
event only works on document
, and there isn't any event that just fires immediately (as far as I can see). So I created one.
$(selector).on('immediate', function() {...});
just calls the function with the relevant element as this
. That's pretty much worthless, but you can combine the events:
$('#editor').on('immediate input', function(){
$('span.wordcount').text(this.value.split(/\s+/).length+' words');
});
And now the word count shows immediately and is live, and remains DRY.
Continue reading ‘Timer events for jQuery’ »
Posted by Danny on September 16, 2013 at 3:22 pm under jQuery.
Comment on this post.