. II 1 - - PowerPoint PPT Presentation

ii
SMART_READER_LITE
LIVE PREVIEW

. II 1 - - PowerPoint PPT Presentation

. II 1 2.1 , .


slide-1
SLIDE 1

Асинхронный код. Часть II

Исаков Федор

1

slide-2
SLIDE 2

Повторение

2.1

slide-3
SLIDE 3

В любой момент времени, любой участок кода исполняется в единственном потоке. 2.2

slide-4
SLIDE 4

Run-to-Completion

let counter = 0; counter++; console.log(counter); // 1

2.3

slide-5
SLIDE 5

Now & Later

console.log('Я выполняюсь сейчас'); setTimeout(() => { console.log('Я выполнюсь позже'); }, 5000); console.log('Я тоже выполняюсь сейчас');

2.4

slide-6
SLIDE 6

Now ...

console.log('Я выполняюсь сейчас'); setTimeout(() => { console.log('Я выполнюсь позже'); }, 5000); console.log('Я тоже выполняюсь сейчас');

2.5

slide-7
SLIDE 7

... & Later

console.log('Я выполняюсь сейчас'); setTimeout(() => { console.log('Я выполнюсь позже'); }, 5000); console.log('Я тоже выполняюсь сейчас');

2.6

slide-8
SLIDE 8

Event Loop

const eventLoop = []; while (true) { if (eventLoop.length > 0) { const event = eventLoop.shift(); event(); } }

2.7

slide-9
SLIDE 9

Инструменты

callback promises async await 3.1

slide-10
SLIDE 10

callback

4.1

slide-11
SLIDE 11

Hey, I'm going to suspend execution for now, but whenever you finish with that network request, and you have some data, please call this function back. 4.2

slide-12
SLIDE 12

Пример

setTimeout(() => { console.log('Я выполнюсь через 5 секунд'); }, 5000);

4.3

slide-13
SLIDE 13

Аргументы

const cb = (error, data) => { if (error) { console.error(error); } else { console.log(data); } }

4.4

slide-14
SLIDE 14

Нелинейный код

console.log('A'); setTimeout(() => { console.log('B'); setTimeout(() => console.log('C'), 5000); console.log('D'); }, 5000); console.log('E');

A → E → B → D → C 4.5

slide-15
SLIDE 15

callback hell

setTimeout(() => { fs.readFile('./path.json', (err, data) => { request(data.url, (err, res, body) => { setTimeout(() => { const data = JSON.parse(body); console.log(data.fact); }, 1000); }); }); }, 5000);

4.6

slide-16
SLIDE 16

4.7

slide-17
SLIDE 17

Задача

Получить текущую температуру воздуха при помощи API погоды

4.8

slide-18
SLIDE 18

Пример

https://api.weather.yandex.ru/v1/forecast? geoid=54 4.9

slide-19
SLIDE 19

index.js

const getWeather = require('./getWeather'); getWeather(54, (error, temp) => { if (error) { console.error(error); } else { console.log(temp); // -3 } });

4.10

slide-20
SLIDE 20

getWeather.js

const request = require('request'); const getWeather = (geoid, cb) => { const url = `https://.../?geoid=${geoid}`; request(url, (err, res, body) => { if (err) { cb(err); } else { const data = JSON.parse(body); cb(null, data.fact.temp); } }); } module.exports = getWeather;

4.11

slide-21
SLIDE 21

Необработанные исключения

const request = require('request'); const getWeather = (geoid, cb) => { const url = `https://.../?geoid=${geoid}`; request(url, (err, res, body) => { if (err) { cb(err); } else { const data = JSON.parse(body); cb(null, data.fact.temp); } }); } module.exports = getWeather;

4.12

slide-22
SLIDE 22

<h1 style="color:red"> Error 404: Not found :-( </h1>

4.13

slide-23
SLIDE 23

Задача

Вычислить среднюю температуру воздуха по области используя API погоды

4.14

slide-24
SLIDE 24

index.js

const getWeather = require('./getWeather'); getWeather(54, (err, t1) => { getWeather(2, (err, t2) => { getWeather(5, (err, t3) => { console.log((t1 + t2 + t3) / 3); }); }); });

4.15

slide-25
SLIDE 25

index.js

const getWeather = require('./getWeather'); console.time('time'); getWeather(54, (err, t1) => { getWeather(2, (err, t2) => { getWeather(5, (err, t3) => { console.log((t1 + t2 + t3) / 3); console.timeEnd('time'); // 691ms }); }); });

4.16

slide-26
SLIDE 26

Последовательно

getWeather(54) getWeather(2) getWeather(5) time 258 513 691

4.17

slide-27
SLIDE 27

Параллельно

getWeather(54) getWeather(2) getWeather(5) time 279

4.18

slide-28
SLIDE 28

index.js

const t = []; const cb = (err, temp) => { t.push(temp); if(t.length === 3) { console.log((t[0] + t[1] + t[2]) / 3); } } getWeather(54, cb); getWeather(2, cb); getWeather(5, cb);

4.19

slide-29
SLIDE 29

Итого

Простая абстракция Нелинейный код callback hell Необработанные исключения Сложный код когда несколько асинхронностей 4.20

slide-30
SLIDE 30

4.21

slide-31
SLIDE 31

Вот бы вместо

const getWeather = require('./getWeather'); getWeather(54, (error, temp) => { if (error) { console.error(error); } else { console.log(temp); } });

5.1

slide-32
SLIDE 32

... можно было писать

const getWeather = require('./getWeather'); getWeather(54) .then(temp => console.log(temp)) .catch(error => console.error(error));

5.2

slide-33
SLIDE 33

... или даже

const getWeather = require('./getWeather'); getWeather(54) .then(console.log) .catch(console.error);

5.3

slide-34
SLIDE 34

... а параллельность так

waitAllAsync([ getWeather(54), getWeather(2), getWeather(5) ]) .then(t => console.log((t[0] + t[1] + t[2]) / 3)) .catch(console.error)

5.4

slide-35
SLIDE 35

promises

  • 5.5
slide-36
SLIDE 36

getWeather.js

const request = require('request'); const getWeather = geoid => new Promise((resolve, reject) => { const url = `https://.../?geoid=${geoid}`; request(url, (err, res, body) => { if (err) { reject(err); } else { const data = JSON.parse(body); resolve(data.fact.temp); } });

5.6

slide-37
SLIDE 37

index.js

const getWeather = require('./getWeather'); getWeather(54) .then(console.log, console.error);

5.7

slide-38
SLIDE 38

promise Fulfilled Rejected resolve() reject() resolved states unresolved states

5.8

slide-39
SLIDE 39

Чейнинг промисов

6.1

slide-40
SLIDE 40

Promise

promise func1 func2 .then( , )

6.2

slide-41
SLIDE 41

Вызов метода .then возвращает новый промис 6.3

slide-42
SLIDE 42

promise func1 func2 func4 func3 .then( , ) .then( , )

6.4

slide-43
SLIDE 43

success

promise func1 func2 func4 func3 .then( , ) .then( , )

6.5

slide-44
SLIDE 44

error

promise func1 func2 func4 func3 .then( , ) .then( , )

6.6

slide-45
SLIDE 45

Хэлперы

const identity = data => data; const thrower = error => { throw error; };

6.7

slide-46
SLIDE 46

const getWeather = require('./getWeather'); getWeather(54) .then(console.log, console.error);

6.8

slide-47
SLIDE 47

const getWeather = require('./getWeather'); getWeather(54) .then(console.log, thrower) .then(identity, console.error);

6.9

slide-48
SLIDE 48

promise console.log console.error .then( , )

6.10

slide-49
SLIDE 49

promise console.log thrower console.error identity .then( , ) .then( , )

6.11

slide-50
SLIDE 50

Задача

Получить температуру воздуха при помощи API погоды и записать результат в файл. 6.12

slide-51
SLIDE 51

getWeather

const request = require('request'); const getWeather = geoid => new Promise((resolve, reject) => { const url = `https://.../?geoid=${geoid}`; request(url, (err, res, body) => err ? reject(err) : resolve(body)); });

6.13

slide-52
SLIDE 52

getWeather(54) .then(JSON.parse, thrower) .then(identity, () => ({ fact: { temp: 0 } })) .then( data => console.log(data.fact.temp), thrower );

6.14

slide-53
SLIDE 53

promise JSON.parse thrower <defaults> identity .then( , ) .then( , ) thrower console.log .then( , )

6.15

slide-54
SLIDE 54

saveToFile

const fs = require('fs'); const saveToFile = data => new Promise((resolve, reject) => { fs.writeFile('./result.json', data, err => err ? reject(err) : resolve('success')); });

6.16

slide-55
SLIDE 55

getWeather(54) .then(JSON.parse, thrower) .then(identity, () => ({ fact: { temp: 0 } })) .then( data => saveToFile(data.fact.temp) .then(console.log, thrower) .then(identity, console.error), thrower );

6.17

slide-56
SLIDE 56

В .then можно передать функцию, которая вернет промис. Выполнение цепочки продолжится когда промис выполнится. 6.18

slide-57
SLIDE 57

getWeather(54) .then(JSON.parse, thrower) .then(identity, () => ({ fact: { temp: 0 } })) .then( data => saveToFile(data.fact.temp), thrower ) .then(console.log, thrower) .then(identity, console.error);

6.19

slide-58
SLIDE 58

promise JSON.parse thrower <defaults> identity .then( , ) .then( , ) thrower saveToFile .then( , ) console.log thrower .then( , ) console.error identity .then( , )

6.20

slide-59
SLIDE 59

getWeather(54) .then(JSON.parse, thrower) .then(identity, () => ({ fact: { temp: 0 } })) .then( data => saveToFile(data.fact.temp), thrower ) .then(console.log, thrower) .then(identity, console.error);

6.21

slide-60
SLIDE 60

getWeather(54) .then(JSON.parse) .catch(() => ({ fact: { temp: 0 } })) .then(data => saveToFile(data.fact.temp)) .then(console.log) .catch(console.error);

6.22

slide-61
SLIDE 61

6.23

slide-62
SLIDE 62

Необработанные исключения

(node:4796) UnhandledPromiseRejectionWarning: Unhandled promise rejection (rejection id: 1): Error: spawn cmd ENOENT [1] (node:4796) DeprecationWarning: Unhandled promise rejections are deprecated. In the future, promise rejections that are not handled will terminate the Node. js process with a non-zero exit code.

6.24

slide-63
SLIDE 63

Promise.resolve

Promise .resolve(' УДАЛЯЕМ Лишние пробелы ') .then(data => data.trim()) .then(data => data.replace(/\s+/g, ' ')) .then(data => data.toLowerCase()) .then(console.log); // удаляем лишние пробелы

7.1

slide-64
SLIDE 64

Promise.reject

Promise .reject('error') .then(identity, console.error); // "error"

7.2

slide-65
SLIDE 65

Promise.all

Promise.all([ getWeather(54), getWeather(2), getWeather(5) ]) .then(t => console.log((t[0] + t[1] + t[2]) / 3)) .catch(console.error);

7.3

slide-66
SLIDE 66

Promise.race

Promise.race([ getWeather(54), getWeather(2), getWeather(5) ]) .then(console.log) .catch(console.error);

7.4

slide-67
SLIDE 67

finally

let isLoading = true Promise.all([ getWeather(54), getWeather(2), getWeather(5) ]) .then(console.log) .catch(console.error) .finally(() => isLoading = false);

7.5

slide-68
SLIDE 68

setTimeout(() => console.log('timeout')); Promise.resolve() .then(() => console.log('promise')); console.log('code');

7.6

slide-69
SLIDE 69

Microtasks

7.7

slide-70
SLIDE 70

7.8

slide-71
SLIDE 71

Итого

Сложная абстракция Более линейный код Избавились от callback hell Нет необработанных исключений Легче писать сложную логику 7.9

slide-72
SLIDE 72

7.10

slide-73
SLIDE 73

7.11

slide-74
SLIDE 74

const Promise = require('bluebird'); Promise .props({ ekb: getWeather(54), spb: getWeather(2), msk: getWeather(5) }) .then(({ ekb, spb, msk }) => { console.log((ekb + spb + msk) / 3); });

bluebird

7.12

slide-75
SLIDE 75

8

slide-76
SLIDE 76

Этот код выглядит хорошо ...

getWeather(54) .then(JSON.parse) .catch(() => ({ fact: { temp: 0 } })) .then(console.log) .catch(console.error);

9.1

slide-77
SLIDE 77

... но так понятнее

try { const body = await getWeather(geoid); return JSON.parse(body); } catch (error) { return { fact: { temp: 0 } }

9.2

slide-78
SLIDE 78

await указывает на то, что нужно дождаться выполнение промиса. Если промис зарезолвился - вернется результат, иначе возникнет исключение. 9.3

slide-79
SLIDE 79

async await

9.4

slide-80
SLIDE 80

const getTempData = async geoid => { try { const body = await getWeather(geoid); return JSON.parse(body); } catch (error) { return { fact: { temp: 0 } }; } }

9.5

slide-81
SLIDE 81

При вызове асинхронной функции получаем promise 9.6

slide-82
SLIDE 82

9.7

slide-83
SLIDE 83

await Promise.resolve(console.log('')); // → SyntaxError: await is only valid in async function

9.8

slide-84
SLIDE 84

const run = async () => { const data = await getTempData(54); return await saveToFile(data.fact.temp); } run() .then(console.log) .catch(console.error);

9.9

slide-85
SLIDE 85

(async function() { await Promise.resolve(console.log('')); // → }());

9.10

slide-86
SLIDE 86

New behavior

Published 08 October 2019 await Promise.resolve(console.log('')); // →

9.11

slide-87
SLIDE 87

const getTempData = async (geoid1, geoid2, geoid3) => { try { const tmp1 = await getWeather(geoid1); const tmp2 = await getWeather(geoid2); const tmp3 = await getWeather(geoid3); return (tmp1 + tmp2 + tmp3) / 3; } catch (error) { return 'Cannot get temperatures'; } }

9.12

slide-88
SLIDE 88

const getTempData = async (geoid1, geoid2, geoid3) => { try { const temps = await Promise.all([ getWeather(geoid1), getWeather(geoid2), getWeather(geoid3) ]); return (temps[0] + temps[1] + temps[2]) / 3; } catch (error) { return 'Cannot get temperatures'; } }

9.13

slide-89
SLIDE 89

Итого

Сложная абстракция Линейный код Избавились от callback hell Нет необработанных исключений Легче писать сложную логику 9.14

slide-90
SLIDE 90

9.15