TE
TechEcho
Home24h TopNewestBestAskShowJobs
GitHubTwitter
Home

TechEcho

A tech news platform built with Next.js, providing global tech news and discussions.

GitHubTwitter

Home

HomeNewestBestAskShowJobs

Resources

HackerNews APIOriginal HackerNewsNext.js

© 2025 TechEcho. All rights reserved.

We have a problem with promises

164 pointsby nolanlabout 10 years ago

22 comments

falcolasabout 10 years ago
Callback heavy languages can be very challenging to think about. They require keeping quite a few disparate pieces in your mental stack all at once.<p>Promises make this both better and worse, in my experience. In the easy cases, all you have to do is follow the promises syntax, and you can treat it as linear code.<p>The problem arises when writing non happy path code, you not only have to think about the promises syntax, but about the abstraction it&#x27;s creating over callbacks. Otherwise, you begin to miss corner cases, create incorrect promises, and in general write code which misbehaves (often silently).<p>Promises are powerful abstractions, but like all abstractions, they have their leaks. Also like all other abstractions, there are places where you have to fully comprehend the mechanisms underneath them to use them properly.
评论 #9564669 未加载
评论 #9564692 未加载
评论 #9565313 未加载
adrusiabout 10 years ago
The real confusion here is dynamic typing. It&#x27;s weird that the parameter to .then() can return any value, but if it&#x27;s a promise there&#x27;s special behavior. And for no good reason either<p><pre><code> p.then(r =&gt; r + 5); </code></pre> Is just a bit of sugar for<p><pre><code> p.then(r =&gt; Promise.resolve(r + 5)); </code></pre> And then the type signatures are easy to reason about. The dynamic typing also risks introducing hard to find bugs. Suppose you have an heterogeneous array `things` which might contain numbers and promises which will resolve to numbers.<p><pre><code> p.then(r =&gt; things[r]) .then(thing =&gt; selectedThings.push(thing)); </code></pre> You might intend `selectedThings` to also contain either numbers or promises that resolve to numbers, ordered deterministically as a function of `p`, but instead it will contain only numbers and its order is nondeterministic (it depends on `p` and all of the promise members of `things` that `p` ever signals).
评论 #9565677 未加载
评论 #9565870 未加载
评论 #9566332 未加载
评论 #9565885 未加载
vkjvabout 10 years ago
&gt; Rookie mistake #3: forgetting to add .catch()<p>I disagree with this advice. I usually refer to this as a &quot;Pokémon Catch&quot; (Gotta&#x27; Catch &#x27;Em All!). Never catch a promise that you are un-able to handle. It&#x27;s better to use a Promise library that supports debugging possibly unhandled exceptions (e.g., Bluebird).<p>&gt; Rookie mistake #4: using &quot;deferred&quot;<p>This one is interesting because it&#x27;s actually used in the Q library documentation for a way to `denodeify` or `promisify` a function. You definitely shouldn&#x27;t do this if it&#x27;s already a promise, but outside of that, it&#x27;s more of a gray area. I tend to recommend using libraries to convert callback functions to promises.
评论 #9565940 未加载
评论 #9565915 未加载
greggmanabout 10 years ago
I would argue that `Promise.all` is not the equivalent of `forEach` because Promise.all will execute everything immediately, maybe all will fail, maybe 1 will fail, eventually you&#x27;ll have your then or your catch called but 100 of your actions will have an attempt to be executed.<p>Compare that to forEach, if one fails and you throw the rest don&#x27;t get executed.<p>I suppose if whatever you&#x27;re doing for each thing is async then they are equivalent equivalent but in general forEach has different sementics than Promise.all
评论 #9565005 未加载
评论 #9564911 未加载
STRMLabout 10 years ago
Great article; a few things I would add. I use bluebird for Promises, which is just the most fantastic Promises lib ever conceived, no joke; if you haven&#x27;t used it try it. So some of these may be Bluebird-specific:<p>1. Don&#x27;t wrap callback functions manually with `new Promise(function(resolve, reject) {...})`, just use `Promise.promisify(...)`. For one-off functions, try `Promise.fromNode(function(cb) { fs.readFile(&#x27;..&#x27;, cb); });`.<p>2. This pattern:<p><pre><code> getUserByName(&#x27;nolan&#x27;).then(function (user) { return getUserAccountById(user.id); }).then(function (userAccount) { &#x2F;&#x2F; I got a user account! }); </code></pre> Could be:<p><pre><code> getUserByName(&#x27;nolan&#x27;) .get(&#x27;id&#x27;) .then(getUserAccountById) .then(function (userAccount) { &#x2F;&#x2F; I got a user account! }); </code></pre> 3. I too used Promise.resolve().then(...) to start a lot of route handlers. Try `Promise.try(function() {...})`, which is equivalent but reduces the temptation to just stick a synchronous value in the `Promise.resolve()` just because you can.<p>4. `Promise#nodeify()` is <i>super</i> useful for creating functions that return promises or use callbacks depending on how they&#x27;re called. For example:<p><pre><code> function getUserName(id, cb) { return db.getUserAsync(id).get(&#x27;name&#x27;) .nodeify(cb); } </code></pre> Is the same as:<p><pre><code> function getUserName(id, cb) { var promise = db.getUserAsync(id).get(&#x27;name&#x27;); if (!cb) return promise; promise.then(function(result) { cb(null, result);}) .catch(function(e) { cb(e); }); } </code></pre> This is great if you want to convert a few functions you use to promises, but they&#x27;re called elsewhere and expect a callback style.<p>I&#x27;m sure there are many more. This is my bible: <a href="https:&#x2F;&#x2F;github.com&#x2F;petkaantonov&#x2F;bluebird&#x2F;blob&#x2F;master&#x2F;API.md" rel="nofollow">https:&#x2F;&#x2F;github.com&#x2F;petkaantonov&#x2F;bluebird&#x2F;blob&#x2F;master&#x2F;API.md</a><p>In short; use Promises! They are the answer for callback hell in Node. Async&#x2F;await may fix more problems in the future but if you want Node&#x2F;Browser compatibility and to get started right now, Promises are the best way to go.
评论 #9564946 未加载
评论 #9564693 未加载
评论 #9565608 未加载
davexunitabout 10 years ago
The messes that callback&#x2F;promise-heavy JavaScript make are a good example of why we need syntactic abstraction in the language. With a proper macro system, this whole async mess could be abstracted away and we could write nice linear code.
评论 #9564876 未加载
评论 #9565926 未加载
johnnymonsterabout 10 years ago
its interesting that when people talk about the newest feature or framework in javascript, they tend to forget about javascript&#x27;s core functionality. It&#x27;s always necessary to look at things like promises with javascript&#x27;s core functionality in mind. Take #3 for example, which is really just a function of the core way javascript executes, taking promises aside!<p>You would not do something like this would you in a normal day at the office? function myFunction(doSomething()){<p>};<p>so why would you do this.<p>Promise.then(doSomething());<p>doSomething() gets immediately invoked when the code is evaluated. It has nothing to do with Promises.<p>Don&#x27;t forget the fundamentals!
评论 #9565302 未加载
评论 #9565806 未加载
LordHumungousabout 10 years ago
&gt;If you know the answer, then congratulations: you&#x27;re a promises ninja<p>No... you know how functions work in javascript.
评论 #9565600 未加载
评论 #9565202 未加载
评论 #9565281 未加载
xtrumanxabout 10 years ago
What&#x27;s so bad about using deferred? In one case I call the resolve or reject parameters and in the other I call a resolve or reject properties on the deferred object. Not much of a difference to me.<p>I learned about deferred a few years ago and kinda stuck with it. It&#x27;s all over my code base and he didn&#x27;t really justify why I should go about changing it. The only thing I can reason I can think of is using his recommendation follows the ES6 spec which doesn&#x27;t matter to me that much for now.
评论 #9564910 未加载
评论 #9565365 未加载
评论 #9565156 未加载
telabout 10 years ago
Re &quot;Advanced Mistake #4&quot;, do Javascript programmers not know about tuples? Does nobody write functions with signatures like<p><pre><code> pair(pa: Promise&lt;A&gt;, pb: (a: A) =&gt; Promise&lt;B&gt;): Promise&lt;{fst: A, snd: B}&gt; { return pa.then(function (a) { return pb(a).map(function (b) { return {fst: a, snd: b} }) }) } </code></pre> It&#x27;s a super reusable little chunk of code.
评论 #9566022 未加载
nolanlabout 10 years ago
If anyone&#x27;s confused by the 4 &quot;puzzles,&quot; I whipped up a JSBin to demonstrate: <a href="http:&#x2F;&#x2F;jsbin.com&#x2F;tuqukakawo&#x2F;1&#x2F;edit?js,console,output" rel="nofollow">http:&#x2F;&#x2F;jsbin.com&#x2F;tuqukakawo&#x2F;1&#x2F;edit?js,console,output</a>
评论 #9565037 未加载
arxpoeticaabout 10 years ago
I made the transition from callbacks to generators&#x2F;iterators recently, and I&#x27;m really enjoying the yield&#x2F;next foo. Promises just never really spoke to me. Not certain why I always felt reticent to use them.
评论 #9564907 未加载
embwbamabout 10 years ago
Typescript or Flow will catch many of these errors. Highly recommended!
评论 #9565003 未加载
评论 #9564901 未加载
telabout 10 years ago
And this is why types are nice. Along with limited overloading.
neuminoabout 10 years ago
Gosh, adding a `.catch(console.log.bind(console))` is just insane.<p>If you have a library that will return non operational error, just remove it from your project. If your code throws when it is not expected to, fix it.<p>This is like saying put a `try&#x2F;catch(ignore)` everywhere. Seriously.
评论 #9565477 未加载
评论 #9566149 未加载
评论 #9565942 未加载
mweibelabout 10 years ago
Very nice article, will keep that in mind when trying to help others with promises. Also helped me to re-understand some things :)<p>One thing though, Advanced mistake #4, is in my opinion good answer, the Q library however gives a (afaik) non-a+-standard way of doing that which I like:<p>from:<p><pre><code> getUserByName(&#x27;nolan&#x27;).then(function (user) { return getUserAccountById(user.id); }).then(function (userAccount) { &#x2F;&#x2F; dangit, I need the &quot;user&quot; object too! }); </code></pre> to:<p><pre><code> getUserByName(&#x27;nolan&#x27;).then(function (user) { return [user, getUserAccountById(user.id)]; }).spread(function (user, userAccount) { &#x2F;&#x2F; I do have the user object here });</code></pre>
tumbling_stoneabout 10 years ago
The most comprehensive and foolproof way is to grab the spec, read the algorithm and fiddle around a day. Sadly this is the only way of fully understand promises, promises already put a lot of cognitive load on your brain when you&#x27;re using them, so having any other abstractions of your own (for remembering how promises work) is bad idea. IMO you&#x27;re better off investing a large continuous block of time for understanding promises rather than reading some article here and there.
janfoehabout 10 years ago
I am stumbling over this:<p>&gt; Just remember: any code that might throw synchronously is a good candidate for a nearly-impossible-to-debug swallowed error somewhere down the line.<p>Why would a synchronously thrown error be swallowed, and why would I not just `try { } catch` here?
评论 #9564866 未加载
ThrustVectoringabout 10 years ago
Puzzle number 3 doesn&#x27;t have a complete explanation. DoSomethingElse() can return a function, which is then evaluated with the result of the first promise as an argument.
tlrobinsonabout 10 years ago
As soon as you can reasonably introduce an ES6&#x2F;7 transpiler into your toolchain you should start using ES7&#x27;s async&#x2F;await or equivalently ES6&#x27;s generators + a coroutine library function like Bluebird.coroutine, Q.async, co, or Task.js.<p>It solves basically all of the problems mentioned in this article.
adhambadrabout 10 years ago
hahahahaha this one cracked me up &quot;Writing code without a stack is a lot like driving a car without a brake pedal: you don&#x27;t realize how badly you need it, until you reach for it and it&#x27;s not there.&quot;
评论 #9564867 未加载
评论 #9566564 未加载
jjaredsimpsonabout 10 years ago
If devs don&#x27;t want to read specs and instead read blog posts then its obvious why there is a problem.
评论 #9565086 未加载