-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathjavascript_promises
711 lines (549 loc) · 39.4 KB
/
javascript_promises
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
Trying to learn javascript promises.
Very good material:
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise
https://davidwalsh.name/promises
http://www.html5rocks.com/en/tutorials/es6/promises/
http://www.sitepoint.com/overview-javascript-promises/
http://www.datchley.name/es6-promises/
https://www.promisejs.org/
http://www.mattgreer.org/articles/promises-in-wicked-detail/
http://pouchdb.com/2015/05/18/we-have-a-problem-with-promises.html (especially see "Advanced mistakes" which is actually a nice collection of good practices)
http://stackoverflow.com/questions/33377614/nolans-promises-a-javascript-puzzles
https://developers.google.com/web/updates/2015/03/introduction-to-fetch
https://davidwalsh.name/fetch
http://blog.soareschen.com/the-problem-with-es6-promises -- good, articulates my concern about return values
https://github.com/soareschen/es6-promise-debugging -- supposedly addresses it? I didn't see it addressed
https://www.stephanboyer.com/post/107/fun-with-promises-in-javascript -- good critique of the "helpful" behavior and how it should have been done (Surprise #1)
Things I definitely don't like, and would do differently in my own implementation:
- automagic assimilation of thenables
- then always waits til the next tick, even if already resolved
==================
http://stackoverflow.com/questions/36178889/after-adding-a-listener-to-a-promise-should-i-use-the-original-promise-or-the-n
Q: after adding a listener to a Promise, should I use the original promise or the new one?
I have some javasript code that takes an existing promise
(say, the promise returned by fetch()) and adds value
(say, then/catch listeners for debugging, or maybe more):
<code>let myFetch = function(url) {
return fetch(url).then(function(value) {
console.log("fetch succeeded: value=",value);
return value;
}.catch(function(reason) {
console.log("fetch failed: reason=",reason);
throw reason;
});
};
</code>
I found myself modifying the above code so that the listeners are only added if some condition is true:
<code>let myFetch = function(url) {
let promise = fetch(url);
if (some condition) {
promise = promise.then(function(value) {
console.log("fetch succeeded: value=",value);
return value;
}.catch(function(reason) {
console.log("fetch failed: reason=",reason);
throw reason;
});
}
return promise;
};
</code>
Now I'm wondering, does it really make sense for myFetch to return the new promise returned by "then"
(actually catch which is shorthand for another "then") as above,
or would it make more sense for it to return the original promise (with the added listeners)?
In other words, I'm thinking of leaving out the second "promise =",
so that the code will look like this instead:
<code>let myFetch = function(url) {
let promise = fetch(url);
if (some condition) {
promise.then(function(value) {
console.log("fetch succeeded: value=",value);
return value;
}.catch(function(reason) {
console.log("fetch failed: reason=",reason);
throw reason;
});
}
return promise;
};
</code>
Is that effectively different from the previous version?
Is either version preferable, and if so, why?
==================
==================
I posted this:
http://stackoverflow.com/questions/35375730/is-javascript-promise-api-more-convoluted-than-it-needs-to-be
Q: Is javascript Promise API more convoluted than it needs to be?
I think I have finally managed to bend my mind around javascript/ES6 Promises, for the most part. It wasn't easy! But there's something that's baffling me about the design.
Why does the Promise constructor take a callback? Given that the callback is called immediately, couldn't the caller just execute that code instead, thereby avoiding one unnecessary level of mind-bending "don't call me, I'll call you"?
Here's what I think of as the prototypical example of Promise usage, copied from Jake Archibald's Javascript Promises tutorial http://www.html5rocks.com/en/tutorials/es6/promises/#toc-promisifying-xmlhttprequest , with comments stripped. It's a Promise-based wrapper for an XMLHttpRequest GET request:
<code>function get(url) {
return new Promise(function(resolve, reject) {
var req = new XMLHttpRequest();
req.open('GET', url);
req.onload = function() {
if (req.status == 200) {
resolve(req.response);
}
else {
reject(Error(req.statusText));
}
};
req.onerror = function() {
reject(Error("Network Error"));
};
req.send();
});
}
</code>
For me, the above code would be much easier to understand if it were rewritten as follows, using a very slightly different kind of promise that I'm imagining, having a no-arg constructor and resolve/reject methods:
<code>function get(url) {
var promise = new MyEasierToUnderstandPromise();
var req = new XMLHttpRequest();
req.open('GET', url);
req.onload = function() {
if (req.status == 200) {
promise.resolve(req.response);
}
else {
promise.reject(Error(req.statusText));
}
};
req.onerror = function() {
promise.reject(Error("Network Error"));
};
req.send();
return promise;
}
</code>
MyEasierToUnderstandPromise is not too hard to implement in terms of Promise. At first I tried making it an actual subclass of Promise, but for some reason I couldn't get that to work; so instead I implemented it as a simple factory function, which returns a plain old Promise object with a couple of extra functions
attached that behave like member functions:
<code>function NewMyEasierToUnderstandPromise() {
var resolveVar;
var rejectVar;
var promise = new Promise(function(resolveParam, rejectParam) {
resolveVar = resolveParam;
rejectVar = rejectParam;
});
promise.resolve = resolveVar;
promise.reject = rejectVar;
return promise;
};
</code>
So, why isn't Promise designed like this? I think if it was, it would have helped me to understand Promises a lot quicker-- I bet it would have cut my learning time in half.
I know a lot of smart people had a hand in making the Promise API, and everyone seems to be generally happy and proud of it, so I'm wondering what they were thinking.
===========================
<code>
function MyEasierToUnderstandPromise() {
Promise.call(this, function(resolve, reject) {
this.resolve = resolve;
this.reject = reject;
});
}
</code>
===============================================================================
Q: Why doesn't javascript Promise support multiple value args?
It makes it awkward to convert old non-promise-based API, e.g.
the new version typically looks like this:
promise.then(function(valueArray) {
let value0 = valueArray[0];
let value1 = valueArray[1];
let value2 = valueArray[2];
...
});
whereas the callback is more naturally written as:
function(value0, value1, value2) { ... }
A: Well, "resolve" and "then" have to return a value, but return values can't
be multiple. Arguably it would have worked if "resolve" and "then"
*always* return an array, but I guess the library designers wanted to
optimize for the common case of a single return value instead.
However, there is something called "spread" in some promise libraries:
http://bluebirdjs.com/docs/api/spread.html
That lets the above be written as:
promise.spread(function(value0, value1, value2) { ... })
However, this is really unnecessary in most cases, as pointed out in the
linked Bluebird doc; it can be rewritten using ES6 destructuring syntax as:
promise.then(function([value0, value1, value2]) { ...})
unfortunately the following doesn't compile :-(
promise.then([value0, value1, value2] => { ... })
===============================================================================
http://stackoverflow.com/questions/36312648/how-does-promise-resolution-decide-whether-to-treat-a-return-value-as-a-promise
Q: how does promise resolution decide whether to treat a return value as a promise or as a plain value?
Promise resolution "helpfully" does different things depending on whether the return value of resolve() or the return value of the function passed to then() is a promise or not.
Therefore, understanding and predicting the behavior requires knowing precisely what criterion is being used (or is allowed to be used) to determine whether something "is a promise" as I use the phrase in the above sentence, so I'd like to nail that down. I'm interested in Promises/A+ in general, and ES6 native promises in particular.
Looking to https://promisesaplus.com/, it says:
1.1 "promise" is an object or function with a "then" method whose behavior conforms to this specification.
To see what that means, I look further into 2.2 "The then method", and find that it rests on a set of
behavior criteria making it clearly impossible to decide algorithmically whether a given object
is a promise or not (proof by Halting Problem). And that's fine; it just means the spec
of the "helpful" behavior in question won't be using the term "is a promise" directly.
So, looking further for the spec of the "helpful" behavior, I find 2.3 "The Promise Resolution Procedure".
Surprise! It *does* use the term "is a promise" directly:
2.3.2 If x is a promise, adopt its state [3.4]
But it saves itself from a descent into meaninglessness in the footnote:
[3.4] Generally, it will only be known that x is a true promise if it comes from the current implementation. This clause allows the use of implementation-specific means to adopt the state of known-conformant promises.
In other words, 2.3.2 didn't really mean "If x is a promise", it really meant (and should have said, IMO) "If x is *known to be* a promise".
But, if I understand correctly, that part is just a shortcut it's allowed to take if it can prove it's safe to do so.
Moving on, the relevant section seems to be 2.3.3, which I summarize as: treat the return value x like a promise
iff x has a property named "then" which is a function.
So then it all rests on the definition of "x.then is a function".
What does that mean, precisely, for the purposes of someone wanting to implement a conformant library,
or someone wanting to predict what a conformant library must/might do when I use it?
Is it the same as saying "typeof x.then === 'function'"?
Hoping for more clues, I looked into the spec of a supposedly-conformant implementation (or set of implementations), ES6 native promises,
which is linked from the Promises doc on MDN.
I believe the relevant section is 25.4.1.3.2: http://www.ecma-international.org/ecma-262/6.0/#sec-promise-resolve-functions.
It looks like the criterion in question is IsCallable(x.then); following the link to that,
I see to my dismay that IsCallable is not an actual function but rather an "abstract operation",
defined in terms of many other "abstract operations" which are far from simple.
At this point, my hope of seeing any conformant reference code which implements the decision in question appears to be rapidly receding :-(
Backing up to my original question "How does promise resolution decide whether to treat a return value as a promise?",
I *think* I've boiled it down, as I explained above, to the more specific question: Exactly what does the Promises/A+ spec mean when it says "is a function"?
If that has a simple clear answer (which of course might allow room for variation among implementations),
follow-up questions would be:
- Does native promises conform to the simple clear answer?
- Why is native promises' implementation of promises/A+'s "is a function" so complicated?
- What's a reasonable way for me to ensure that my return value will be treated as a promise as I intend,
or as a plain value as I intend?
=======================================================================================
Q: in https://github.com/promises-aplus/promises-spec/issues/94#issuecomment-16239382
it says "This also feeds into discussions of flattening promise chains; it has previously been discussed over and over that a promise-for-a-thenable is a problematic pattern in JavaScript and should not be supported. (A promise for another monad, of course, is fine.)".
Can someone provide pointers to where this has been discussed over and over?
(I asked)
PA: see the two links from here: https://github.com/promises-aplus/promises-spec/issues/101 :
https://gist.github.com/ForbesLindesay/5392612 "Arguments in favor of Promises for Promises"
https://gist.github.com/ForbesLindesay/5392701 "Arguments against Promises for Promises"
Hmm, not very readable :-(
But they link to great discussions!
https://mail.mozilla.org/pipermail/es-discuss/2013-April/030192.html
https://mail.mozilla.org/pipermail/es-discuss/2013-April/030198.html (puzzle for anti-flatteners)
And here's a puzzle for pro-flatteners:
http://stackoverflow.com/questions/37426037/promises-for-promises-that-are-yet-to-be-created-without-using-the-deferred-ant
More possible references for promise-for-a-promise:
https://github.com/promises-aplus/promises-spec/issues/97#issuecomment-16319350
Should I be looking at this "fantasy land"?
https://github.com/fantasyland/fantasy-promises
Wow, the implementation is really small.
Hmm, this defines "future" as the strongly typed non-flattening version of promise: https://github.com/kriskowal/gtor/#user-content-singular-and-plural
==========================================
Relevant links to the problem in question:
http://stackoverflow.com/questions/36312648/how-does-promise-resolution-decide-whether-to-treat-a-return-value-as-a-promise
http://stackoverflow.com/questions/27746304/how-do-i-tell-if-an-object-is-a-promise/27746324#comment67620804_27746324
http://stackoverflow.com/questions/29435262/regarding-promises-a-specification-what-is-the-difference-between-the-terms-t/29435437#comment67807705_29435437
http://stackoverflow.com/questions/23803743/what-is-the-explicit-promise-construction-antipattern-and-how-do-i-avoid-it
http://stackoverflow.com/questions/32168194/fulfill-dont-resolve-promise-with-another-promise?noredirect=1&lq=1
http://stackoverflow.com/questions/37959086/how-to-stop-automatic-resolution-of-promise-objects
Wed Oct 26 19:26:15 PDT 2016
I filed this bug against the spec:
https://github.com/promises-aplus/promises-spec/issues/240
==========================================
Title: change "if x is a promise" to something non-circular and clear
The promises/A+ spec currently has a clause saying "If x is a promise, adopt its state [3.4]:"
where footnote [3.4] is: "Generally, it will only be known that x is a true promise if it comes from the current implementation.
This clause allows the use of implementation-specific means to adopt the state of known-conformant promises."
Notice that, according to the earlier definition of "promise",
"If x is a promise" means "If x is an object or function with a 'then' method whose behavior conforms to this specification",
where "this specification" includes the clause in question.
In other words, the definition of "promise" is circular.
If [3.4] is an attempt to clarify, it fails.
The clause in question should be changed to something non-circular and clear:
- Do not use the words "if x is a promise" or any other self-referential phrase here.
- If the clause is to say "if SOMETHING(x), adopt its state", then clearly say what is meant by SOMETHING(x).
In other words, if it's intended that the implementation has latitude here, then clearly say how much latitude,
so that it will be meaningful to ask, and possible to tell, whether any particular straightforward implementation
is conformant or not from looking at the source code.
In particular, it should be possible to discern whether each of the following possible
implementations of the boolean function SOMETHING(x) is conformant:
- function(x) { return typeof(x.then) == 'function'; }
- function(x) { return typeof(x.then) === 'function'; }
- function(x) { return x instanceof SomePromisesImplementation.Promise
|| x instanceof SomeOtherPromisesImplementation.WhateverItCallsAPromise; }
- function(x) { return false; }
- function(x) { return true; }
- function(x) { return Math.random() >= 0.5; }
==========================================
Wed Oct 26 19:42:51 PDT 2016
Added the following comment:
==========================================
I think one reasonable possible resolution would be to change "If x is a promise, adopt its state [3.4]:"
to "**(Optional)** If x is **known to be** a promise, adopt its state [3.4]:".
Even though this is still self-referential, I believe this will make it clear and meaningful.
In particular, the questions of whether the 6 SOMETHING(x) functions shown at the end of my original post are conformant can then be answered:
NO, NO, YES (assuming we trust the two implementations are conformant to the entire spec), YES, NO, NO.
==========================================
Wed Oct 26 21:37:29 PDT 2016
adding another comment to http://stackoverflow.com/questions/27746304/how-do-i-tell-if-an-object-is-a-promise/27746324
@BenjaminGruenbaum , are you aware that the Promises/A+ spec to which you're referring also has an earlier section `2.3.2. If x is a promise, adopt its state [3.4]:`?
This is a clearly different section from 2.3.3.3, with different intent and necessarily different criteria.
So the definitive-sounding first section of your answer ("How a promise library decides: If it has a `.then` function - that's the *only* standard promise libraries use") appears to be quite incorrect.
[and subsequent not-entirely-pleasant discussion]
[I should write my own answer to this one]
Maybe wait til my pull is accepted, see:
https://github.com/promises-aplus/promises-spec/issues/240
https://github.com/promises-aplus/promises-spec/pull/241
My answer:
It's difficult to tell what the context is here,
and the OP hasn't responded to requests for clarification.
Other people have answered based on assumed guesses of the intended context,
but those guesses don't seem to be the most likely interpretations to me,
so I'm adding my own answer based on my own guesses of what I think the question
most likely means.
I won't consider interpretations that make the question silly or invalid,
since I think those interpretations are ungenerous and not helpful to anyone.
In that spirit, I can think of two reasonable interpretations of the question:
1. Where the Promises/A+ spec currently says: "If `x` is a promise, adopt its state",
how does/should/must the implementation test "If `x` is a promise"?
First of all, note that, in this spec, "promise" has a precise meaning; it's defined as:
"an object or function with a then method whose behavior conforms to this specification".
Therefore "If x is a promise" is obviously not a condition that can be decided,
so it was a poor choice of words to use in these decision-making instructions.
The wording was recently fixed [https://github.com/promises-aplus/promises-spec/issues/240],
so the spec actually no longer contains this problematic phrase.
[XXX NOT! reword this in some tactful way]
The new wording is:
"(Recommended) If `x` is known to be a promise, adopt its state [3.4]",
where footnote 3.4 now says:
"Generally, it will only be known that `x` is a true promise if it comes from the current implementation. This clause allows the use of implementation-specific means to adopt the state of known-conformant promises, which may be identified by a test such as `x instanceof Promise`. This allows optimisations by not requiring the more general thenable-handling procedure with its repeated value inspection."
Hopefully that's now clear.
Note that there is also a section soon after that, for handling "thenables"
(defined as anything with a "then" method) that are *not* known to be promises.
That section is fairly clear, specific and detailed, so refer to it if you're interested in that.
2. How can I make sure the promise library will treat
the value I return from my "then" callback as a plain value,
rather than as a promise or something promise-like, which it's not?
According to the Promises/A+ spec, the two situations in which the promise resolution procedure
may do something other than "fulfill `promise` with `x`" are when:
- `x` is a known to be a promise (using some test such as `x instanceof Promise`)
- `x` has a `then` method.
Note, however, that the former (`x` is a promise) implies the latter (`x` has a `then` method),
so for this concern, it suffices to test the latter condition.
In other words, if your interest is in making sure some plain value
you're about to return from your `then` callback
isn't going to be (mis)treated as a thenable,
just check whether your value has a `then` method.
If it doesn't, great. If it does, or if you can't predict
whether it will or won't when you're writing the code,
then you'll have to protect the value, e.g. by wrapping it in a non-thenable
and requiring the consumer to unwrap it.
Other questions and answers show some possible ways to do such wrapping; e.g.:
http://stackoverflow.com/questions/25028365/how-do-i-create-a-javascript-promise-which-resolves-to-a-thenable#answer-25029165
http://stackoverflow.com/questions/32168194/fulfill-dont-resolve-promise-with-another-promise?noredirect=1&lq=1#answer-32285123
http://stackoverflow.com/questions/37959086/how-to-stop-automatic-resolution-of-promise-objects#answer-37960913
Q: promise "then" callback order question
Consider the following code:
let p = Promise.resolve(42);
p.then(() => console.log("a")).then(() => console.log("b"));
p.then(() => console.log("1")).then(() => console.log("2"));
What are the possible outputs?
I know "b" and "1" must come after "a", and "2" must come after "1".
Given that, there are three possible outputs:
a b 1 2
a 1 b 2
a 1 2 b
Which of these are actually possible, according to the ES6 spec?
Which are possible in a promises/A+ conformant implementation?
A: All of them are possible. See http://stackoverflow.com/questions/36870467/what-is-the-order-of-execution-in-javascript-promises
Holy moly, huge answer from jfriend00. Not sure why it's all necessary.
Tue Nov 1 17:58:31 PDT 2016
bleah, domenic doesn't like my https://github.com/promises-aplus/promises-spec/pull/241
Hey @domenic, thanks for taking a look.
I'm certainly disappointed that you and I see such different things when we look
at that section of the spec, especially after I thought we were on the verge of making
what I perceive to be a quite good and important fix.
If you haven't found anything that's been said so far to be compelling,
I doubt anything I could say would convince you that there's a problem,
so I won't try to argue it further.
For the record, my feeling on the relative importances of the pieces of this proposed change is:
1. Adding "(Recommended)" and "known to be" to the main text is essential--
without those, the stated requirement is impossible to satisfy, and doesn't express your intent.
2. Including the example criterion `x instanceof Promise` in the footnote is secondary--
it definitely adds value, but I wouldn't say the spec is broken without it.
3. I'm not attached to anything else about the content or rephrasing of the footnote,
though I do like what we've arrived at in the proposed patch at this moment-- it's very clear to me now.
@bergus's latest suggestion of only adding "(Recommended)" would address so little of the problem,
as I see it, that I would not want to have my name on that commit.
So, I'll retract this pull request, and I'll close https://github.com/promises-aplus/promises-spec/issues/240 citing your https://github.com/promises-aplus/promises-spec/pull/241#issuecomment-257607015 , unless you want to do that.
Let me know; I'll close it in a day or two if I don't hear otherwise from you.
Q: why that crap about waiting for platform code before executing then callbacks??
A: really good stuff here, should be in the spec:
http://stackoverflow.com/questions/38059284/why-does-javascript-promise-then-handler-run-after-other-code?rq=1#answer-38062501
Thu Nov 10 23:35:28 PST 2016
Added this to #241:
-------------------------------------
After thinking more about this, I'm going to give it one more pitch.
Since the proposed patch contents seem to be stable,
and the discussion is back to the higher-level question of whether to do this at all,
I'm going to take it out of this trench and back up to the issue thread
#240. See you there.
Added this to #241:
-------------------------------------
I made the pull request (#241) and revised it a few times based on review discussion. I'm now very happy with it; I think it completely resolves the problem.
For reference, the proposed new wording is:
(Recommended) If `x` is known to be a promise, adopt its state [3.4]:
...
[3.4] Generally, it will only be known that `x` is a true promise if it comes from the current implementation. This clause allows the use of implementation-specific means to adopt the state of known-conformant promises, which may be identified by a test such as `x instanceof Promise`. This allows optimisations by not requiring the more general thenable-handling procedure with its repeated value inspection.
It looks to me like @ForbesLindesay, @bergus, and @briancavalier are on board with the above proposed new wording.
But @domenic isn't; in his Nov 1 comment:
> In general I am -1 on this change. I don't think the current spec is ambiguous and people haven't had any issues implementing it [many times].
So we're back to the question of whether to do anything about this issue at all.
I'll try to make one more appeal for it here. Here goes.
---------------------------------------------------------------
@domenic, I get that you think this part of the spec is good enough and not worth putting much more thought into.
The others (@ForbesLindesay, @bergus, @briancavalier) have put thought into it, and they've arrived at this tighter version of this section that I think is really good and completely resolves the issue.
Unless you actually think this change makes it *worse* overall, would you be willing to let the change through?
Here's why I think it's important.
The world is learning how promises work, in large part through discussions that refer to the promises/A+ spec, since it's the primary and best reference. In fact, it's the *only* reference that has the level of precision and completeness that's appropriate and useful for most of these discussions.
(In contrast, the ES6 promises spec, for example, in addition to being implementation-specific, uses language that is too complicated and convoluted for it to be useful in resolving most of the questions I've seen.)
In particular, the wording of this section becomes significant when I discuss your spec with people, e.g. when asking or answering questions on stackoverflow, or if I write an implementation, or when I review someone else's implementation.
Without the proposed tightening, I find myself saying things of the form:
According to the promises/A+ spec: `[old wording]`,
which obviously can't be taken literally since that wouldn't make sense,
but I've talked to the spec authors about it
and I can tell you with confidence that they did have something
coherent in mind here, and what they really meant was: `[new wording]`.
which is kind of wordy and embarrassing.
Sure, people have managed to implement it [many times] in spite of this hurdle, and maybe all of them even inferred your intent correctly-- I haven't checked.
In any case, after this change, I'll be able to say the following instead, which I very much prefer:
According to the promises/A+ spec: `[new wording]`.
That's a significant reduction in noise and distraction which will increase the quality of the global discussion, and it will also reflect better on you and on the great work you've put into this project.
What do you think? Will you let it through?
-------------------------------------
Tue Dec 6 21:31:39 PST 2016
Going to file yet another bug report, on the compliance suite.
See Bergi's answers to: http://stackoverflow.com/questions/35391179/promises-a-compliant
http://stackoverflow.com/questions/27746304/how-do-i-tell-if-an-object-is-a-promise
Maybe file a bug. What would be a good title?
Issue: sloppy language in descriptions of compliance test suite
Issue: misleading language in descriptions of compliance test suite
Issue: misleading language in descriptions of what compliance test suite does <--- favoring this one
Issue: hyperbole in descriptions of what compliance test suite does
Issue: misleading descriptions of what compliance test suite does
Issue: false and misleading descriptions of what compliance test suite does
Issue: false and misleading descriptions compliance test suite
Issue: false and misleading language in descriptions of compliance test suite <-- or this one
Issue: false and misleading language in descriptions of what compliance test suite does <-- or this one
Issue: does conformance/compliance refer to the spec or to the test suite?
Both the Compliance Test Suite doc and the Conformant Implementations list
make statements saying or implying that passing the test suite is equivalent to complying with the spec.
Such statements are both false and misleading.
[NOTE: The terms "compliance (with the spec)" and "conformance (with the spec)" seem to be used interchangeably; I'll follow suit, under the assumption that everyone agrees they are synonyms.]
Details:
* In https://promisesaplus.com/implementations :
There are many conformant implementations of Promises/A+.
Here are the ones we know about.
Note that an implementation’s conformance is determined by it passing the test suite.
Um, no. An implementation's conformance to a spec is determined by whether it does what the spec says it must do, every time, for every possible input.
That's a stricter criterion than passing any test suite, no matter how thorough the test suite may be.
* In https://github.com/promises-aplus/promises-tests :
This suite tests compliance of a promise implementation with the Promises/A+ specification.
Passing the tests in this repo means that you have a Promises/A+ compliant implementation of the then() method, [...]
Of course that equivalence doesn't hold-- no test suite could possibly accomplish that;
this is something that should be clear after a moment's
thought by anyone who has taken an introductory computer science course.
The most that can be said truthfully is something like:
- the test suite will catch many common errors in implementations
- if the test suite fails, the implementation is not conformant
- if the test suite passes, the implementation *might* be conformant, but obviously no test suite can ever tell you for sure.
(This glosses over the possibility of bugs in the suite and timeouts, which is fine; and it assumes spec bugs such as https://github.com/promises-aplus/promises-spec/issues/240
have been fixed.)
One might be tempted to dismiss this issue as simply informal/loose/sloppy language
that, anyone can see, should not be taken literally since that would be absurd.
The problem with that is that the hyperbole is viral-- it leaks out into discussions
on the web, which become at best confusing and at worst seriously misleading.
In particular, it seems that the test suite is now routinely given as a supposedly definitive answer to
"is [such-and-such promise implementation] promises/A+ compliant?"; such answers are wrong and misleading,
and are apparently contributing to what looks like a mass delusion.
Examples:
- Bergi's comment in http://stackoverflow.com/questions/35391179/promises-a-compliant#comment-58501762 :
[...] just run the test suite on the implementation in question to get a definitive answer.
- End of Benjamin Gruenbaum's answer http://stackoverflow.com/questions/27746304/how-do-i-tell-if-an-object-is-a-promise#answer-27746324:
*really* need to be sure? You can always run it through the test suite :D
I actually don't know whether the smiley is because Benjamin is acknowledging the test suite can't really tell
in general, or whether he actually believes it *can* tell and the smiley is just because of the non-practicality issue.
JLRishe seems to interpret it as the latter (see below), so the misinformation snowballs...
- JLRishe's comment in http://stackoverflow.com/questions/27746304/how-do-i-tell-if-an-object-is-a-promise#comment-44650451:
The only alternative as far as I can see is to take Benjamin Gruenbaum's suggestion and run it through the promise test suite. But that's not practical for actual production code.
It would be helpful if more accurate language could be chosen which does not lead so easily to misconceptions and misinformation.
Tue Feb 14 17:28:55 PST 2017
wanting to ask something about why the "then" delays.
it's asked here:
http://stackoverflow.com/questions/31225687/why-promise-resolve-then-is-delayed
What should I reply to Benjamin Bruenbaum's?
Maybe this:
I haven't yet heard an explanation for why this particular race is considered so terrible.
In general principle, if I don't know when a promise is going to be resolved, then I don't
know when its `then` callback will be called relative to any other code it's not chained to...
so why was it so important for the spec to nail down this one special case,
in spite of both increased implementation complexity and performance cost?
E.g. consider `myPromise.then(function(){console.log("a");}); myOtherPromise.then(function(){console.log("b");})`.
That's another case when you get sometimes `a b` and sometimes `b a`. Is that terrible too?
Or this:
I'm not really following what's so terrible about this particular race.
In general, I can't assume anything about the order in which my `then` callback
will be called relative to any other code it's not chained to... so why was it so
important for the spec to nail down this one special case,
in spite of both implementation complexity and performance cost?
E.g. consider `myPromise.then(function(){console.log("a");}); myOtherPromise.then(function(){console.log("b");})`.
That's another case when you get sometimes `a b` and sometimes `b a`. Is that terrible too?
OH WAIT-- I need to read the link he talked about:
http://blog.izs.me/post/59142742143/designing-apis-for-asynchrony
which defers to a second link:
https://blog.ometer.com/2011/07/24/callbacks-synchronous-and-asynchronous/
A lot of it is about JVM and locking, which is irrelevant in the javascript same-thread context,
I think. Is there any analogous thing about resource acquiring? I.e. what gets easier
if the callback is guaranteed to be done after the stack is unwound?
Ok one valid point is testability: if it can ever be delayed, then make sure it's *always* delayed
so the test suite is more likely to cover it.
Sat May 20 00:23:48 PDT 2017
I want to ask yet another question...
Here, asked this:
Q: can Promises/A+ promises be leveraged to implement synchronous-when-already-resolved semantics?
I'm implementing a data structure in memory
that shadows part of a large data structure stored somewhere out on the web.
Let's say the data structures in question are binary trees.
I want the in-memory tree to initially consist of just the root node,
and it should grow lazily by fetching nodes from the web on demand, as the user (or algorithm) explores.
One natural way to do this is for the tree node data type to provide methods `getLeftChild()`, `getRightChild()`,
each of which immediately returns a *promise* for the respective child node.
When `getLeftChild()` is called on a tree node whose left child is already in memory,
it returns a promise that is already-resolved with the cached child;
otherwise it initiates a fetch of the child and returns a promise for it,
and when the fetched child eventually comes back from the web, the fetched child will be saved in memory for the future and used to resolve the promise.
So, to print the node 5 levels down the left branch, I'd say:
root.getLeftChild().then(child0 =>
child0.getLeftChild().then(child00 =>
child00.getLeftChild().then(child000 =>
child000.getLeftChild().then(child0000 =>
child0000.getLeftChild().then(child00000 => {
console.log("child00000 = ",child00000);
})))));
Or, the same thing using `async/await`:
(async()=>{
let child0 = await root.getLeftChild();
let child00 = await child0.getLeftChild();
let child000 = await child00.getLeftChild();
let child0000 = await child000.getLeftChild();
let child00000 = await child0000.getLeftChild();
console.log("child00000 = ",child00000);
})();
This all works fine, and the calling code doesn't look too horrible in either case.
My only misgiving is that, when exploring within parts of the binary tree (or any similar linked data structure)
that are already in memory, I don't want to suffer the overhead of initiating a new microtask
every time I want to go from one node to a neighbor in the in-memory data structure.
Think of an algorithm whose core computation does millions of such link-following operations.
[Promises/A+](https://promisesaplus.com/) does indeed require a new microtask (at least) for each `then` callback execution:
> 2.2.4 onFulfilled or onRejected must not be called until the execution context stack contains only platform code. [3.1].
I believe `async/await` has a similar requirement.
What I'd like to know is: what's the easiest/cleanest way to make
a Promise-like object that behaves exactly like a Promises/A+ promise, *except* for clause 2.2.4?
I.e. I want it to have a `then` (or `then`-like) method that is "synchronous-when-available", so that the first code snippet above
will execute in one shot without yielding the execution context.
To avoid naming issues/confusion, I'm happy to refrain from calling my synchronous-when-available accessor `then`
(which is effectively a reserved word nowadays thanks to Promises/A+); instead, I'll call it `thenOrNow`.
And I'll call my hypothetical type/implementation `PromiseOrNow`.
Would I have to write `PromiseOrNow` from scratch, or is there a neat and reliable way to leverage an existing Promises/A+ implementation such as native `Promise`?
Notice that, since I'm not planning to mess with anything named "`then`",
`PromiseOrNow` could incidentally be Promises/A+ compliant, if that turns out to be a good way of doing it.
Perhaps it would be a prototype interited from the native `Promise.prototype`.
These properties would be nice in some ways, but they are not requirements.
Fri Jan 26 17:10:20 PST 2018