FrontTalks: Михаил Давыдов (Яндекс), «Promise – это не больно»

52

description

Асинхронность в JavaScript-приложениях – обычное дело. Любой обмен данными – асинхронный, что HTTP, что чтение файла, что БД. Все просто, если запрос один – callback, и все дела. Если логика сложнее, то приложение в худшем случае превращается в «Callback Pyramid of Doom» или обрастает разной магией. Promise – это подход, который выпрямляет вложенные запросы, превращает «асинхронную лапшу» в структурированный код и делает ваше приложение лучше. Вы всё еще боитесь использовать Promise? Тогда приходите на мой доклад.

Transcript of FrontTalks: Михаил Давыдов (Яндекс), «Promise – это не больно»

Page 1: FrontTalks: Михаил Давыдов (Яндекс), «Promise – это не больно»
www.princexml.com
Prince - Non-commercial License
This document was created with Prince, a great way of getting web content onto paper.
Page 2: FrontTalks: Михаил Давыдов (Яндекс), «Promise – это не больно»

Promise —это небольно!Михаил Давыдов, JavaScript-разработчик

2013 год, FrontTalks

Page 3: FrontTalks: Михаил Давыдов (Яндекс), «Promise – это не больно»

Асинхронность везде!

Page 4: FrontTalks: Михаил Давыдов (Яндекс), «Promise – это не больно»

Запросы к серверу

04

Page 5: FrontTalks: Михаил Давыдов (Яндекс), «Promise – это не больно»

Данные пользователя

05

Page 6: FrontTalks: Михаил Давыдов (Яндекс), «Promise – это не больно»

События интерфейса

GA

RM

OS

HK

A

06

Page 7: FrontTalks: Михаил Давыдов (Яндекс), «Promise – это не больно»

Таймеры и анимация

07

Page 8: FrontTalks: Михаил Давыдов (Яндекс), «Promise – это не больно»
Page 9: FrontTalks: Михаил Давыдов (Яндекс), «Promise – это не больно»

Когда один callback —всё здорово!

Page 10: FrontTalks: Михаил Давыдов (Яндекс), «Promise – это не больно»

Последовательные запросыlogin('user:pass@server', function (err, server) {

if (err) return cb(err);

server.open('db', function (err, db) {

if (err) return cb(err);

db.query(query, function (err, view) {});

});

});

01.

02.

03.

04.

05.

06.

07.

10

Page 11: FrontTalks: Михаил Давыдов (Яндекс), «Promise – это не больно»

Параллельные запросыvar rows = [], total = view.length;

function fetch(err, row) {

if (err) return cb(err);

if (rows.length === total) cb(null, rows);

}

for (var i = 0; i < total; i++) {

view.get(i, fetch);

}

01.

02.

03.

04.

05.

06.

07.

08.

11

Page 12: FrontTalks: Михаил Давыдов (Яндекс), «Promise – это не больно»

Шумlogin('user:pass@server', function (err, server) {

if (err) return cb(err);

server.open('db', function (err, db) {

if (err) return cb(err);

db.query(query, function (err, view) {});

});

});

01.

02.

03.

04.

05.

06.

07.

12

Page 13: FrontTalks: Михаил Давыдов (Яндекс), «Promise – это не больно»

Шум● Статус ошибки

● Однообразная обработка ошибки

● Глубина вложенности

13

Page 14: FrontTalks: Михаил Давыдов (Яндекс), «Promise – это не больно»

Сложно отменить действиеdb.query(query, function (err, view) {});

Как отменить или игнорировать запрос в этом случае?

db.query(query, fn).abort(); // .cancel() ?

db.query(query, function (err) {

if (err.message === 'abort') return;

});

01.

01.

02.

03.

04.

14

Page 15: FrontTalks: Михаил Давыдов (Яндекс), «Promise – это не больно»

Несколько обработчиковquery(query1, function (err, view) {});

query(query2, function (err, view) {});

query(query3, function (err, view) {});

3 запроса — 3 попытки логина. Для кэша нужно еще дописать код.

01.

02.

03.

15

Page 16: FrontTalks: Михаил Давыдов (Яндекс), «Promise – это не больно»

Делегирование результатаfunction uberQueryGenerator(data) {

// Готовим queryFromData

return query(queryFromData);

}

function uberQueryGenerator(data, fn) {

query(queryFromData, fn);

}

01.

02.

03.

04.

01.

02.

03.

16

Page 17: FrontTalks: Михаил Давыдов (Яндекс), «Promise – это не больно»

У callback-ов нетединого интерфейса

Page 18: FrontTalks: Михаил Давыдов (Яндекс), «Promise – это не больно»

Нет единого интерфейса● Последний аргумент

● Свойство конфига или метод

– success

– complete

– done

– finish

– ???

18

Page 19: FrontTalks: Михаил Давыдов (Яндекс), «Promise – это не больно»

Нет единого интерфейса$('div').animate({}, function () {});

// А можно еще вот так

$('div').animate({}, {

complete: function () {}

});

01.

02.

03.

04.

05.

19

Page 20: FrontTalks: Михаил Давыдов (Яндекс), «Promise – это не больно»

Нет единого интерфейса$.ajax({

url: url

success: function () {}

});

$.ajax({

url: url

}).done(function () {});

01.

02.

03.

04.

05.

06.

07.

20

Page 21: FrontTalks: Михаил Давыдов (Яндекс), «Promise – это не больно»

Магия

Page 22: FrontTalks: Михаил Давыдов (Яндекс), «Promise – это не больно»

Магия● Step.js

● Streamline.js

● Fibers

● ???

22

Page 23: FrontTalks: Михаил Давыдов (Яндекс), «Promise – это не больно»

Step.jsStep(function login() {

login('user:pass@server', this);

}, function openDatabase(err, db) {

if (err) throw err;

db.query(query, this);

});

01.

02.

03.

04.

05.

06.

23

Page 24: FrontTalks: Михаил Давыдов (Яндекс), «Promise – это не больно»

Streamline.jsvar db = login('user:pass@server', _);

db.query(query, _);

Вроде бы ничего!

01.

02.

24

Page 25: FrontTalks: Михаил Давыдов (Яндекс), «Promise – это не больно»

Streamline.js(fstreamline__.create(function(_) {

var db = (yield fstreamline__

.invoke(null, login, ['user:pass@server', _], 1));

(yield fstreamline__.invoke(db, "query", [query, _], 1));

;yield;}, 0).call(this, function(err) {

if (err) throw err;

}));

01.

02.

03.

04.

05.

06.

07.

25

Page 26: FrontTalks: Михаил Давыдов (Яндекс), «Promise – это не больно»
Page 27: FrontTalks: Михаил Давыдов (Яндекс), «Promise – это не больно»

Promise● aka Futures

● Распостранен в других языках

● Стандартизирован Promise/A+

● Стандарт «Покрыт тестами» Promises/A+ Compliance Test Suite

● Готовые абстракции над fs, http, ...

27

Page 28: FrontTalks: Михаил Давыдов (Яндекс), «Promise – это не больно»

Promiselogin('user:pass@server')

.then(function (server) {

return server.open('db');

})

.then(function (db) {

return db.query(query);

})

01.

02.

03.

04.

05.

06.

07.

28

Page 29: FrontTalks: Михаил Давыдов (Яндекс), «Promise – это не больно»

Promise.then(function (view) {

return fetchRows(view);

}, function handleError(err) {

console.error(err);

}) // ...

Меньше шума, обработка ошибок в одном месте

01.

02.

03.

04.

05.

29

Page 30: FrontTalks: Михаил Давыдов (Яндекс), «Promise – это не больно»

Абстракция над обещаннымиданными

● Обещание можно сдержать, а можно не выполнять

● Состояние изменяется только 1 раз

● Результат сохраняется

● Цепочка обещаний

● Можно пообещать нескольким

30

Page 31: FrontTalks: Михаил Давыдов (Яндекс), «Promise – это не больно»

Реализация Promisevar Promise = function () {

this._value = null;

this.isFulfilled = false; // ok

this.isRejected = false; // fail

this.isResolved = false; // ok || fail

// ...

};

01.

02.

03.

04.

05.

06.

07.

31

Page 32: FrontTalks: Михаил Давыдов (Яндекс), «Promise – это не больно»

Реализация PromisePromise.prototype = {

// @return {Promise}

then: function (onFulfilled, onRejected) {},

fulfill: function (data) {},

reject: function (error) {}

};

01.

02.

03.

04.

05.

06.

32

Page 33: FrontTalks: Михаил Давыдов (Яндекс), «Promise – это не больно»

Использование Promise// @param {Number} time

// @return {Promise}

function timeout(time) {

var promise = new Promise();

setTimeout(promise.fulfill, time);

return promise;

}

01.

02.

03.

04.

05.

06.

07.

33

Page 34: FrontTalks: Михаил Давыдов (Яндекс), «Promise – это не больно»

Пример timeouttimeout(1000)

.then(function () {

console.log('first');

return timeout(1000);

})

.then(console.log.bind(console, 'second!'));

01.

02.

03.

04.

05.

06.

Run

34

Page 35: FrontTalks: Михаил Давыдов (Яндекс), «Promise – это не больно»

Пример timeouttimeout(1000)

.then(randomLog)

.then(console.log.bind(console, 'done!'));

function randomLog() {

var rnd = Math.random();

console.log(rnd);

if (rnd < 0.5) return timeout(2000);

}

01.

02.

03.

04.

05.

06.

07.

08.Run

35

Page 36: FrontTalks: Михаил Давыдов (Яндекс), «Promise – это не больно»

Promise● Не хак и не магия

● Единый интерфейс всех промисов

● Меньше шума в коде

● Разделение логики на шаги

● . . .

● PROFIT!

36

Page 37: FrontTalks: Михаил Давыдов (Яндекс), «Promise – это не больно»

Трюки с Promise

Page 38: FrontTalks: Михаил Давыдов (Яндекс), «Promise – это не больно»

Склеивание Promise// $.when - агрегатор Promise

// Результат не раньше, чем через 1с

$.when($.get('/'), $.get('/?'), timeout(1000))

.then(function (res) {

console.log(res[0].length + res[1].length);

});

01.

02.

03.

04.

05.

06.

Run

38

Page 39: FrontTalks: Михаил Давыдов (Яндекс), «Promise – это не больно»

Повтор Promisenew Attempt(get404, repeat3Times)

.then(ok, epicFail, progress);

function get404() {

return $.get('/404');

}

function repeat3Times(err, num) {

if (num < 4) return 1000;

}

01.

02.

03.

04.

05.

06.

07.

08.Run

39

Page 40: FrontTalks: Михаил Давыдов (Яндекс), «Promise – это не больно»

Прозрачный кэш с Promisevar cache;

function request() {

return cache ? cache : cache = $.get('/');

}

request().then(function (html) {

console.log(html.length);

});

01.

02.

03.

04.

05.

06.

07.Run

40

Page 42: FrontTalks: Михаил Давыдов (Яндекс), «Promise – это не больно»
Page 43: FrontTalks: Михаил Давыдов (Яндекс), «Promise – это не больно»

Generators● Решение проблем асинхронности будущего

● Оператор yield «Ставит выполнение кода на паузу»

● Когда?

– Node.js 0.11.x c --harmony-generators

– Браузеры на V8 3.19 c --harmony-generators

– Firefox 2+ (старый синтаксис)

● Можно транслировать в ES5, но лучше это не делать...

43

Page 44: FrontTalks: Михаил Давыдов (Яндекс), «Promise – это не больно»

Generatorsvar co = require('co');

co(function *() {

var server = yield login('user:pass@server'),

db = yield server.open('db'),

view = yield db.query(query);

});

Скоро на экранах ваших IDE

01.

02.

03.

04.

05.

06.

44

Page 45: FrontTalks: Михаил Давыдов (Яндекс), «Promise – это не больно»

yield vs then?

Page 46: FrontTalks: Михаил Давыдов (Яндекс), «Promise – это не больно»

Generators и Promise● Именно «и», а не «VS»

● Используем вместе

– Библиотека Q

– Библиотека co

● С Generators код еще чище!

● Обработка ошибок с try/catch!

46

Page 47: FrontTalks: Михаил Давыдов (Яндекс), «Promise – это не больно»

Используйте Promise!● Готов к использованию сегодня!

● Единый интерфейс

● Стандарт Promise/A+

● Структурирует код

● Меньше шума в коде

● Куча библиотек: Vow, Q, jQuery

47

Page 48: FrontTalks: Михаил Давыдов (Яндекс), «Promise – это не больно»
Page 49: FrontTalks: Михаил Давыдов (Яндекс), «Promise – это не больно»
Page 51: FrontTalks: Михаил Давыдов (Яндекс), «Promise – это не больно»

Почитать● Примеры с презентации

● Promises/A+

● Differences from Promises/A

● Design of Q library (настоятельно рекомендую)

● Iterators & Generators

● A Study on Solving Callbacks with JavaScript Generators

● A Closer Look at Generators Without Promises

51

Page 52: FrontTalks: Михаил Давыдов (Яндекс), «Promise – это не больно»

clck.ru/8i9pr