ุงู„ู…ู†ุงุทู‚ ููŠ ุฏุงุฑุช: ุฌุฑุงุญุฉ ุงู„ู‚ู„ุจ ุงู„ู…ูุชูˆุญ ู„ู„ู…ุญูŠุท

ู…ุฑุญุจุง! ุงุณู…ูŠ ุฏูŠู…ุง ูˆุฃู†ุง ู…ุทูˆุฑ ุฃู…ุงู…ูŠ ููŠ Wrike. ู†ูƒุชุจ ุฌุฒุก ุงู„ุนู…ูŠู„ ู…ู† ุงู„ู…ุดุฑูˆุน ููŠ Dart ุŒ ูˆู„ูƒู† ุนู„ูŠู†ุง ุงู„ุนู…ู„ ู…ุน ุนู…ู„ูŠุงุช ุบูŠุฑ ู…ุชุฒุงู…ู†ุฉ ู„ุง ุชู‚ู„ ุนู† ุงู„ุชู‚ู†ูŠุงุช ุงู„ุฃุฎุฑู‰. ุงู„ู…ู†ุงุทู‚ ู‡ูŠ ูˆุงุญุฏุฉ ู…ู† ุงู„ุฃุฏูˆุงุช ุงู„ู…ููŠุฏุฉ ุงู„ุชูŠ ูŠูˆูุฑู‡ุง Dart ู„ุฐู„ูƒ. ูˆู„ูƒู† ููŠ ู…ุฌุชู…ุน Dart ุŒ ู†ุงุฏุฑู‹ุง ู…ุง ุชุฌุฏ ู…ุนู„ูˆู…ุงุช ู…ููŠุฏุฉ ุนู†ู‡ุง ุŒ ู„ุฐู„ูƒ ู‚ุฑุฑุช ุฃู† ุฃูู‡ู… ูˆุฃุฎุจุฑูƒ ุงู„ู…ุฒูŠุฏ ุนู† ู‡ุฐู‡ ุงู„ุฃุฏุงุฉ ุงู„ู‚ูˆูŠุฉ.





ุฅุฎู„ุงุก ุงู„ู…ุณุคูˆู„ูŠุฉ: ุฌู…ูŠุน ุงู„ูƒูˆุฏ ุงู„ู…ุณุชุฎุฏู… ููŠ ู‡ุฐู‡ ุงู„ู…ู‚ุงู„ุฉ ูŠุชุธุงู‡ุฑ ูู‚ุท ุจุฃู†ู‡ ู„ุตู‚ ูˆู„ุตู‚. ููŠ ุงู„ูˆุงู‚ุน ุŒ ู„ู‚ุฏ ุจุณู‘ุทุชู‡ุง ูƒุซูŠุฑู‹ุง ูˆุชุฎู„ุตุช ู…ู† ุงู„ุชูุงุตูŠู„ ุงู„ุชูŠ ู„ุง ูŠู†ุจุบูŠ ุงู„ุงู†ุชุจุงู‡ ุฅู„ูŠู‡ุง ููŠ ุณูŠุงู‚ ู‡ุฐู‡ ุงู„ู…ู‚ุงู„ุฉ. ููŠ ุฅุนุฏุงุฏ ุงู„ู…ุงุฏุฉ ุŒ ุงุณุชุฎุฏู…ู†ุง Dart ุงู„ุฅุตุฏุงุฑ 2.7.2 ูˆ AngularDart ุงู„ุฅุตุฏุงุฑ 5.0.0.

Dart . . , dart:async, .



Wrike ( AngularDart) :



// Part of AngularDart component class
final NgZone _zone;
final ChangeDetectorRef _detector;
final Element _element;

void onSomeLifecycleHook() {
  _zone.runOutsideAngular(() {
    _element.onMouseMove.where(filterEvent).listen((event) {
      doWork(event);
      _zone.run(_detector.markForCheck);
    });
  });
}


Dart- , . , .



, , , :



  • API , .
  • .
  • ( , ).


Dart , issues github. API, , , , , DartUP. , .



, :



  • package:intl;
  • package:quiver;
  • package:angular.


.



Intl



intl โ€” . : , , message plural .



:



class AppIntl {
  static String loginLabel() => Intl.message(
        'Login',
        name: 'AppIntl_loginLabel',
      );
}


. , , . , - . withLocale, :



// User has 'en' as default locale, but he works from Russia
final fallbackLocale = 'ru';

Future<Duration> parseText(String userText) async =>
    // Try to parse user text
    await _parseText(userText) ??
    // Try to parse with 'ru' locale if default parsing failed
    await Intl.withLocale(fallbackLocale, () => _parseText(userText));

// This is actual parser
Future<Duration> _parseText(String userText) {
  // ...
}


-, fallback .



, withLocale , , . !



parseText Future, , . , - , . โ€” , . โ€” . .



, Future , , . .



1. Future



โ€” ! - Future:



class Future {
  Future() : _zone = Zone.current; // Save current zone on creation

  final Zone _zone;
  // ...
}


Future . . , then:



class Future {
  // ...
  Future<R> then<R>(
    FutureOr<R> callback(T value), // ...
  ) {
    // Notify zone about callback for async operation
    callback = Zone.current.registerUnaryCallback(callback);
    final result = Future();
    // Schedule to complete [result] when async operation ends
    _addListener(_FutureListener.then(
      result,
      callback, // ...
    ));
    return result;
  }
}

class _FutureListener {
  // ...
  FutureOr<T> handleValue(S value) =>
      // Call scheduled work inside zone that was saved in [result] Future
      result._zone.runUnary(_callback, value);
}


! , . Future, . Future , โ€” Zone.current. runUnary . , , , . , - !



2. , , ยซ ยป



It's an execution context. โ€” Brian Ford, zone.js author.

โ€” , ยซยป: . , , . Future , , run*. -.



โ€” , _current. Zone.current โ€” _current. :



class Zone {
  static Zone _current = _rootZone; // This is where current zone lives

  static Zone get current => _current;
  // ...
}


, . run* : run, runUnary, runBinary. _current:



class Zone {
  // ...
  R run<R>(R action()) {
    Zone previous = _current;
    // Place [this] zone in [_current] for a while
    _current = this;
    try {
      return action(); // Then do stuff we wanted
    } finally {
      _current = previous; // Then revert current zone to previous
    }
  }
}


_current , . Zone.current .



! , , current , :



class _FutureListener {
  // ...
  FutureOr<T> handleValue(T value) => result._zone.runUnary(_callback, value);
}

class _FutureListener {
  // ...
  FutureOr<T> handleValue(T value) {
    final previousZone = Zone.current;
    Zone.current = result._zone;
    final updatedValue = _callback(value);
    Zone.current = previousZone;
    return updatedValue;
  }
}


. run* , , , . . .



, Intl , . - .



3. Intl



withLocale:



class Intl {
  // ...
  static withLocale(String locale, Function() callback) =>
      // Create new zone with saved locale, then call callback inside it
      runZoned(callback, zoneValues: {#Intl.locale: locale});
}


- ! .



runZoned . , runZoned . run* .



, โ€” zoneValues. , . zoneValues ( Symbol).



, :



class Intl {
  // ...
  static String getCurrentLocale() {
    // Get locale from current zone
    var zoneLocale = Zone.current[#Intl.locale];
    return zoneLocale == null ? _defaultLocale : zoneLocale;
  }
  // ...
}


! , . , . - , .



[], ( โ€” ). []= โ€” , , . - withLocale runZoned:



class Intl {
  // ...
  static withLocale(String locale, Function() callback) =>
      // Create new zone with saved locale, then call callback inside it
      runZoned(callback, zoneValues: {#Intl.locale: locale});
}


, .



, :



// User has 'en' as default locale, but he works from Russia
final fallbackLocale = 'ru';

Future<Duration> parseText(String userText) async =>
    // Try to parse user text
    await _parseText(userText) ??
    // Try to parse with 'ru' locale if default parsing failed
    await Intl.withLocale(fallbackLocale, () => _parseText(userText));

// This is actual parser
Future<Duration> _parseText(String userText) {
  // ...
}


, withLocale , . , Future . _parseText _parseText. !



, Future . Future, Stream Timer ยซยป . , . .



FakeAsync



- -. , . Dart . , test, . , Future test, expect, Future :



void main() {
  test('do some testing', () {
    return getAsyncResult().then((result) {
      expect(result, isTrue);
    });
  });
}


โ€” . , debounce , . ยซยป mock , .



, . , . package:quiver FakeAsync.



:



import 'package:quiver/testing/async.dart'; 

void main() {
  test('do some testing', () {
    // Make FakeAsync object and run async code with it
    FakeAsync().run((fakeAsync) {
      getAsyncResult().then((result) {
        expect(result, isTrue);
      });
      // Ask FakeAsync to flush all timers and microtasks
      fakeAsync.flushTimers();
    });
  });
}


FakeAsync, , . .



, .



1. FakeAsync



run :



class FakeAsync {
  // ...
  dynamic run(callback(FakeAsync self)) {
    // Make new zone if there wasn't any zone created before
    _zone ??= Zone.current.fork(specification: _zoneSpec);
    dynamic result;
    // Call the test callback inside custom zone
    _zone.runGuarded(() {
      result = callback(this);
    });
    return result;
  }
}


โ€” , .



โ€” fork specification.



Dart โ€” root. , โ€” Zone.root. root, root . run, ?



class Zone {
  // ...
  R run<R>(R action()) {
    Zone previous = _current;
    // Place [this] zone in [_current] for a while
    _current = this;
    try {
      return action(); // Then do stuff we wanted
    } finally {
      _current = previous; // Then revert current zone to previous
    }
  }
}


ยซ ยป. :



class _RootZone implements Zone {
  // Only root zone can change current zone
  // ...
  R _run<R>(Zone self, ZoneDelegate parent, Zone zone, R action()) {
    Zone previous = Zone._current;
    // On this [zone] the .run() method was initially called
    Zone._current = zone;
    try {
      return action(); // Then do stuff we wanted
    } finally {
      Zone._current = previous; // Then revert current zone to previous
    }
  }
}


!



โ€” root . , - .



2. zoneSpecification



ZoneSpecification โ€” zoneValues:



abstract class ZoneSpecification {
  // All this handlers can be added during object creation
  // ...
  HandleUncaughtErrorHandler get handleUncaughtError;
  RunHandler get run;
  RunUnaryHandler get runUnary;
  RunBinaryHandler get runBinary;
  RegisterCallbackHandler get registerCallback;
  RegisterUnaryCallbackHandler get registerUnaryCallback;
  RegisterBinaryCallbackHandler get registerBinaryCallback;
  ErrorCallbackHandler get errorCallback;
  ScheduleMicrotaskHandler get scheduleMicrotask;
  CreateTimerHandler get createTimer;
  CreatePeriodicTimerHandler get createPeriodicTimer;
  PrintHandler get print;
  ForkHandler get fork;
}


, , . โ€” -. , .



โ€” , - :



// This is the actual type of run handler
typedef RunHandler = R Function<R>(
  Zone self, // Reference to the zone with this specification
  ZoneDelegate parent, // Object for delegating work to [self] parent zone
  Zone zone, // On this zone .run() method was initially called
  R Function() action, // The actual work we want to run in [zone]
);

int _counter = 0;

final zone = Zone.current.fork(
  specification: ZoneSpecification(
    // This will be called within [zone.run(doWork);]
    run: <R>(self, parent, zone, action) {
      // RunHandler
      // Delegate an updated work to parent, so in addition
      // to the work being done, the counter will also increase
      parent.run(zone, () {
        _counter += 1;
        action();
      });
    },
  ),
);

void main() {
  zone.run(doWork);
}


. run , run. - โ€” , .



.



, . , -. .



. - , ยซยป . , , , , root . , .



โ€” , .



โ€” , ยซยป . , root , _current .



โ€” , . , , , . , .



, . :





: , , D B



, .



3. - FakeAsync



FakeAsync. , run , . :



class FakeAsync {
  // ...
  ZoneSpecification get _zoneSpec => ZoneSpecification(
        // ...
        scheduleMicrotask: (_, __, ___, Function microtask) {
          _microtasks.add(microtask); // Just save callback
        },
        createTimer: (_, __, ___, Duration duration, Function callback) {
          // Save timer that can immediately provide its callback to us
          var timer = _FakeTimer._(duration, callback, isPeriodic, this);
          _timers.add(timer);
          return timer;
        },
      );
}


scheduleMicrotask. , - , . , Future , Future . : Stream .



FakeAsync c โ€” .



createTimer. createTimer, , , Timer. : ยซ ?ยป. :



abstract class Timer {
  factory Timer(Duration duration, void callback()) {
    // Create timer with current zone
    return Zone.current
        .createTimer(duration, Zone.current.bindCallbackGuarded(callback));
  }
  // ...
  // Create timer from environment
  external static Timer _createTimer(Duration duration, void callback());
}

class _RootZone implements Zone {
  // ...
  Timer createTimer(Duration duration, void f()) {
    return Timer._createTimer(duration, f);
  }
}


โ€” , . , , . FakeAsync: _FakeTimer, โ€” .



class FakeAsync {
  // ...
  ZoneSpecification get _zoneSpec => ZoneSpecification(
        // ...
        scheduleMicrotask: (_, __, ___, Function microtask) {
          _microtasks.add(microtask); // Just save callback
        },
        createTimer: (_, __, ___, Duration duration, Function callback) {
          // Save timer that can immediately provide its callback to us
          var timer = _FakeTimer._(duration, callback, isPeriodic, this);
          _timers.add(timer);
          return timer;
        },
      );
}


run, . , . , . FakeAsync โ€” , .



, ! flushTimers:



class FakeAsync {
  // ...
  void flushTimers() {
    // Call timer callback for every saved timer
    while (_timers.isNotEmpty) {
      final timer = _timers.removeFirst();
      timer._callback(timer);
      // Call every microtask after processing each timer
      _drainMicrotasks();
    }
  }

  void _drainMicrotasks() {
    while (_microtasks.isNotEmpty) {
      final microtask = _microtasks.removeFirst();
      microtask();
    }
  }
}


, , , . , !



, , ZoneSpecification. .



:



  • (handleUncaughtError, errorCallback);
  • (registerCallback*);
  • (createPeriodicTimer);
  • (print);
  • (fork).


, . , , . AngularDart โ€” , .



!




All Articles