Про зоны в Dart

Алексей Золотых

Разница в зоне в контексте

Dart наследник JavaScript
Многопоточность vs Однопоточность

var SharedMap = make(map[string]string)
func changeMap(value string) {
    SharedMap["test"] = value
}
func main() {
    go changeMap("value1")
    go changeMap("value2")
    time.Sleep(time.Millisecond * 500)
    fmt.Println(SharedMap["test"])
}
            

public class StreamSum {
    static final int SUM = IntStream.range(0, 100)
      .parallel()
      .reduce((n, m) -> n + m).getAsInt();

    public static void main(String[] args) {
        System.out.println(SUM);
    }
}
            

Паттерн Event Loop


let SharedMap = {};
const changeMap = (value) => {
    return () => {
        SharedMap["test"] = value
    }
}
// 0 Timeout means we are making new Task in Queue for next cycle
setTimeout(changeMap("value1"), 0);
setTimeout(changeMap("value2"), 0);
setTimeout(()=>{
   console.log(SharedMap["test"])
}, 500);
        
  • Windows applications
  • X Window System (Xlib event loop)
  • GLib event loop (GTK)
  • macOS Core Foundation run loops

Node.js

Это может быть еще и быстро

Все хорошо, но...


var server = await HttpServer.bind(InternetAddress.loopbackIPv4, 8080);

await for (var request in server) {
    request.response
            ..write('Hello, world')
            ..close();
}
            

var server = await HttpServer.bind(InternetAddress.loopbackIPv4, 8080);

await for (var request in server) {
    request.response
            ..write('Hello, world')
            ..close();
}
            

Напишем зоны на JS


class Context {
    constructor(parentContext) {}
    fork() {}
    bind(fn) {}
    run(fn) {}
}
            

Конструктор


    constructor(parentContext) {
        let context;

        if (parentContext) {
            // Создаем копию
            context = Object.create(parentContext)
            context.parent = parentContext;
        } else {
            // Возвращаем текущий контекст
            context = this;
        }
        return context;
    }
            

Метод fork


    fork() {
        // Возвращаем копию
        return new Context(this);
    }
            

Метод bind


    bind(fn) {
        // Получаем текущий контекст
        const context = this.fork();
        // Возвращаем функцию в которой уже замкнут контекст
        return () => {
            return context.run(() => fn.apply(this, arguments), this, arguments);
        }
    }
            

Метод run


    run(fn) {
        // Заменяем текущий контекст на наш
        let oldContext = context;
        context = this;
        const result = fn.call() // Выполняем функцию в контексте
        context = oldContext; // Возвращаем как было
        return result; // Результат выполнения
    }
            

context = new Context();

var bkp = window.setTimeout; // Подменяем setTimeout

context.setTimeout = (callback, time) => {
	callback = context.bind(callback);
	return bkp.call(
        window,
        callback.bind(context), time
    );
};

window.setTimeout = function (){
	return context.setTimeout.apply(this, arguments);
};
            

context.fork({}).run(() => {

	context.message = 'Привет!';
	setTimeout(() => {
	  console.log(context.message);
	}, 0);

});

console.log(context.message);
            

context.fork({}).run(() => {

        context.message = 'Привет!';
	setTimeout(() => {
	  console.log(context.message);
	}, 0);

});

console.log(context.message);
            

context.fork({}).run(() => {
    
    context.message = 'Привет!';
    setTimeout(() => {
	  console.log(context.message);
    }, 0);
    
});

console.log(context.message);
            

Возможности

Отправлять ошибки

runZoned<Future<void>>(() async {
  runApp(CrashyApp());
}, onError: (error, stackTrace) {
  // Whenever an error occurs, call the `_reportError` function. This sends
  // Dart errors to the dev console or Sentry depending on the environment.
  _reportError(error, stackTrace);
});
            

Future<void> _reportError(dynamic error, dynamic stackTrace) async {
  // Print the exception to the console.
  print('Caught error: $error');
  if (isInDebugMode) {
    // Print the full stacktrace in debug mode.
    print(stackTrace);
    return;
  } else {
    // Send the Exception and Stacktrace to Sentry in Production mode.
    _sentry.captureException(
      exception: error,
      stackTrace: stackTrace,
    );
  }
}
            
DI

Zone.current[MyClass] = new MyClassImpl()
Zone.current[MyClass]
            
Tests

void main() {
  test("Future.timeout() throws an error once the timeout is up", () {
    fakeAsync((async) {
      expect(new Completer().future.timeout(new Duration(seconds: 5)),
          throwsA(new isInstanceOf()));
      async.elapse(new Duration(seconds: 5));
    });
  });
}
            

Выводы?