Promise-subclassing.html (7745B)
1 <!doctype html> 2 <meta charset=utf-8> 3 <title></title> 4 <script src=/resources/testharness.js></script> 5 <script src=/resources/testharnessreport.js></script> 6 <script> 7 8 var theLog = []; 9 var speciesGets = 0; 10 var speciesCalls = 0; 11 var constructorCalls = 0; 12 var constructorGets = 0; 13 var resolveCalls = 0; 14 var rejectCalls = 0; 15 var thenCalls = 0; 16 var catchCalls = 0; 17 var allCalls = 0; 18 var raceCalls = 0; 19 var nextCalls = 0; 20 21 function takeLog() { 22 var oldLog = theLog; 23 theLog = []; 24 speciesGets = speciesCalls = constructorCalls = resolveCalls = 25 rejectCalls = thenCalls = catchCalls = allCalls = raceCalls = 26 nextCalls = constructorGets = 0; 27 return oldLog; 28 } 29 30 function clearLog() { 31 takeLog(); 32 } 33 34 function log(str) { 35 theLog.push(str); 36 } 37 38 class LoggingPromise extends Promise { 39 constructor(func) { 40 super(func); 41 Object.defineProperty(this, "constructor", 42 { 43 get: function() { 44 ++constructorGets; 45 log(`Constructor get ${constructorGets}`); 46 return Object.getPrototypeOf(this).constructor; 47 } 48 }); 49 ++constructorCalls; 50 log(`Constructor ${constructorCalls}`); 51 } 52 53 static get [Symbol.species]() { 54 ++speciesGets; 55 log(`Species get ${speciesGets}`); 56 return LoggingSpecies; 57 } 58 59 static resolve(val) { 60 ++resolveCalls; 61 log(`Resolve ${resolveCalls}`); 62 return super.resolve(val); 63 } 64 65 static reject(val) { 66 ++rejectCalls; 67 log(`Reject ${rejectCalls}`); 68 return super.reject(val); 69 } 70 71 then(resolve, reject) { 72 ++thenCalls; 73 log(`Then ${thenCalls}`); 74 return super.then(resolve, reject); 75 } 76 77 catch(handler) { 78 ++catchCalls; 79 log(`Catch ${catchCalls}`); 80 return super.catch(handler); 81 } 82 83 static all(val) { 84 ++allCalls; 85 log(`All ${allCalls}`); 86 return super.all(val); 87 } 88 89 static race(val) { 90 ++raceCalls; 91 log(`Race ${raceCalls}`); 92 return super.race(val); 93 } 94 } 95 96 class LoggingIterable { 97 constructor(array) { 98 this.iter = array[Symbol.iterator](); 99 } 100 101 get [Symbol.iterator]() { return () => this } 102 103 next() { 104 ++nextCalls; 105 log(`Next ${nextCalls}`); 106 return this.iter.next(); 107 } 108 } 109 110 class LoggingSpecies extends LoggingPromise { 111 constructor(func) { 112 ++speciesCalls; 113 log(`Species call ${speciesCalls}`); 114 super(func) 115 } 116 } 117 118 class SpeciesLessPromise extends LoggingPromise { 119 static get [Symbol.species]() { 120 return undefined; 121 } 122 } 123 124 promise_test(function testBasicConstructor() { 125 var p = new LoggingPromise((resolve) => resolve(5)); 126 var log = takeLog(); 127 assert_array_equals(log, ["Constructor 1"]); 128 assert_true(p instanceof LoggingPromise); 129 return p.then(function(arg) { 130 assert_equals(arg, 5); 131 }); 132 }, "Basic constructor behavior"); 133 134 promise_test(function testPromiseRace() { 135 clearLog(); 136 var p = LoggingPromise.race(new LoggingIterable([1, 2])); 137 var log = takeLog(); 138 assert_array_equals(log, ["Race 1", "Constructor 1", 139 "Next 1", "Resolve 1", "Constructor 2", 140 "Then 1", "Constructor get 1", "Species get 1", "Species call 1", "Constructor 3", 141 "Next 2", "Resolve 2", "Constructor 4", 142 "Then 2", "Constructor get 2", "Species get 2", "Species call 2", "Constructor 5", 143 "Next 3"]); 144 assert_true(p instanceof LoggingPromise); 145 return p.then(function(arg) { 146 assert_in_array(arg, [1, 2]); 147 }); 148 }, "Promise.race behavior"); 149 150 promise_test(function testPromiseRaceNoSpecies() { 151 clearLog(); 152 var p = SpeciesLessPromise.race(new LoggingIterable([1, 2])); 153 var log = takeLog(); 154 assert_array_equals(log, ["Race 1", "Constructor 1", 155 "Next 1", "Resolve 1", "Constructor 2", 156 "Then 1", "Constructor get 1", 157 "Next 2", "Resolve 2", "Constructor 3", 158 "Then 2", "Constructor get 2", 159 "Next 3"]); 160 assert_true(p instanceof SpeciesLessPromise); 161 return p.then(function(arg) { 162 assert_in_array(arg, [1, 2]); 163 }); 164 }, "Promise.race without species behavior"); 165 166 promise_test(function testPromiseAll() { 167 clearLog(); 168 var p = LoggingPromise.all(new LoggingIterable([1, 2])); 169 var log = takeLog(); 170 assert_array_equals(log, ["All 1", "Constructor 1", 171 "Next 1", "Resolve 1", "Constructor 2", 172 "Then 1", "Constructor get 1", "Species get 1", "Species call 1", "Constructor 3", 173 "Next 2", "Resolve 2", "Constructor 4", 174 "Then 2", "Constructor get 2", "Species get 2", "Species call 2", "Constructor 5", 175 "Next 3"]); 176 assert_true(p instanceof LoggingPromise); 177 return p.then(function(arg) { 178 assert_array_equals(arg, [1, 2]); 179 }); 180 }, "Promise.all behavior"); 181 182 promise_test(function testPromiseResolve() { 183 clearLog(); 184 var p = LoggingPromise.resolve(5); 185 var log = takeLog(); 186 assert_array_equals(log, ["Resolve 1", "Constructor 1"]); 187 var q = LoggingPromise.resolve(p); 188 assert_equals(p, q, 189 "Promise.resolve with same constructor should preserve identity"); 190 log = takeLog(); 191 assert_array_equals(log, ["Resolve 1", "Constructor get 1"]); 192 193 var r = Promise.resolve(p); 194 log = takeLog(); 195 assert_array_equals(log, ["Constructor get 1"]); 196 assert_not_equals(p, r, 197 "Promise.resolve with different constructor should " + 198 "create a new Promise instance (1)") 199 var s = Promise.resolve(6); 200 var u = LoggingPromise.resolve(s); 201 log = takeLog(); 202 assert_array_equals(log, ["Resolve 1", "Constructor 1"]); 203 assert_not_equals(s, u, 204 "Promise.resolve with different constructor should " + 205 "create a new Promise instance (2)") 206 207 Object.defineProperty(s, "constructor", { value: LoggingPromise }); 208 var v = LoggingPromise.resolve(s); 209 log = takeLog(); 210 assert_array_equals(log, ["Resolve 1"]); 211 assert_equals(v, s, "Faking the .constructor should work"); 212 assert_false(v instanceof LoggingPromise); 213 214 var results = Promise.all([p, q, r, s, u, v]); 215 return results.then(function(arg) { 216 assert_array_equals(arg, [5, 5, 5, 6, 6, 6]); 217 }); 218 }, "Promise.resolve behavior"); 219 220 promise_test(function testPromiseReject() { 221 clearLog(); 222 var p = LoggingPromise.reject(5); 223 var log = takeLog(); 224 assert_array_equals(log, ["Reject 1", "Constructor 1"]); 225 226 return p.catch(function(arg) { 227 assert_equals(arg, 5); 228 }); 229 }, "Promise.reject behavior"); 230 231 promise_test(function testPromiseThen() { 232 clearLog(); 233 var p = LoggingPromise.resolve(5); 234 var log = takeLog(); 235 assert_array_equals(log, ["Resolve 1", "Constructor 1"]); 236 237 var q = p.then((x) => x*x); 238 log = takeLog(); 239 assert_array_equals(log, ["Then 1", "Constructor get 1", "Species get 1", 240 "Species call 1", "Constructor 1"]); 241 assert_true(q instanceof LoggingPromise); 242 243 return q.then(function(arg) { 244 assert_equals(arg, 25); 245 }); 246 }, "Promise.then behavior"); 247 248 promise_test(function testPromiseCatch() { 249 clearLog(); 250 var p = LoggingPromise.reject(5); 251 var log = takeLog(); 252 assert_array_equals(log, ["Reject 1", "Constructor 1"]); 253 254 var q = p.catch((x) => x*x); 255 log = takeLog(); 256 assert_array_equals(log, ["Catch 1", "Then 1", "Constructor get 1", 257 "Species get 1", "Species call 1", "Constructor 1"]); 258 assert_true(q instanceof LoggingPromise); 259 260 return q.then(function(arg) { 261 assert_equals(arg, 25); 262 }); 263 }, "Promise.catch behavior"); 264 265 </script>