| // Copyright (c) 2015, the Dart project authors. Please see the AUTHORS file |
| // for details. All rights reserved. Use of this source code is governed by a |
| // BSD-style license that can be found in the LICENSE file. |
| |
| // Test that async functions don't zone-register their callbacks for each |
| // await. Async functions should register their callback once in the beginning |
| // and then reuse it for all awaits in their body. |
| // This has two advantages: it is faster, when there are several awaits (on |
| // the Future class from dart:async), and it avoids zone-nesting when tracing |
| // stacktraces. |
| // See http://dartbug.com/23394 for more information. |
| |
| import 'dart:async'; |
| import 'package:expect/expect.dart'; |
| import 'package:async_helper/async_helper.dart'; |
| |
| Future<int> gee(int i) async { |
| return await i; |
| } |
| |
| Stream<int> bar() async* { |
| var i = 0; |
| while (true) yield await gee(i++); |
| } |
| |
| awaitForTest() async { |
| var sum = 0; |
| await for (var x in bar().take(100)) { |
| sum += x; |
| } |
| Expect.equals(4950, sum); |
| } |
| |
| awaitTest() async { |
| await null; |
| await null; |
| await null; |
| await null; |
| await null; |
| await null; |
| await null; |
| await null; |
| await null; |
| await null; |
| await null; |
| await null; |
| await null; |
| await null; |
| await null; |
| await null; |
| await null; |
| await null; |
| await null; |
| await null; |
| await null; |
| await null; |
| await null; |
| await null; |
| await null; |
| await null; |
| await null; |
| await null; |
| await null; |
| await null; |
| await null; |
| await null; |
| await null; |
| await null; |
| await null; |
| await null; |
| await null; |
| await null; |
| await null; |
| await null; |
| return await 499; |
| } |
| |
| runTests() async { |
| await awaitTest(); |
| await awaitForTest(); |
| } |
| |
| var depth = 0; |
| |
| var depthIncreases = 0; |
| |
| increaseDepth() { |
| depthIncreases++; |
| depth++; |
| // The async/await code should not register callbacks recursively in the |
| // then-calls. As such the depth should never grow too much. We don't want |
| // to commit to a specific value, since implementations still have some |
| // room in how async/await is implemented, but 20 should be safe. |
| Expect.isTrue(depth < 20); |
| } |
| |
| ZoneCallback<R> registerCallback<R>( |
| Zone self, ZoneDelegate parent, Zone zone, R f()) { |
| var oldDepth = depth; |
| increaseDepth(); |
| return parent.registerCallback(zone, () { |
| depth = oldDepth; |
| return f(); |
| }); |
| } |
| |
| ZoneUnaryCallback<R, T> registerUnaryCallback<R, T>( |
| Zone self, ZoneDelegate parent, Zone zone, R f(T arg)) { |
| var oldDepth = depth; |
| increaseDepth(); |
| return parent.registerUnaryCallback(zone, (x) { |
| depth = oldDepth; |
| return f(x); |
| }); |
| } |
| |
| ZoneBinaryCallback<R, T1, T2> registerBinaryCallback<R, T1, T2>( |
| Zone self, ZoneDelegate parent, Zone zone, R f(T1 arg1, T2 arg2)) { |
| var oldDepth = depth; |
| increaseDepth(); |
| return parent.registerBinaryCallback(zone, (x, y) { |
| depth = oldDepth; |
| return f(x, y); |
| }); |
| } |
| |
| void sm(Zone self, ZoneDelegate parent, Zone zone, f) { |
| var oldDepth = depth; |
| increaseDepth(); |
| return parent.scheduleMicrotask(zone, () { |
| depth = oldDepth; |
| return f(); |
| }); |
| } |
| |
| main() { |
| asyncStart(); |
| var desc = new ZoneSpecification( |
| registerCallback: registerCallback, |
| registerUnaryCallback: registerUnaryCallback, |
| registerBinaryCallback: registerBinaryCallback, |
| scheduleMicrotask: sm); |
| var future = runZoned(runTests, zoneSpecification: desc); |
| future.then((_) => asyncEnd()); |
| } |