Asynchronous JavaScript

Що значить "(а)синхронно"?

В програмуванні - два види "одночасності"
паралелізм
vs.
серіалізм (aka співвимірність, aka concurrency)
Q: Чи потрібний нам справжній паралелізм?
A: Майже завжди ні.
якщо операції не пов'язані між собою - who cares? 🤷♀️
якщо пов'язані - можемо явно задавати їх порядок (детермінізм)
Виконання-до-завершення (run-to-completion)
//Excerpt From: Kyle Simpson. “You Don't Know JS: Async & Performance.”
var a = 1;
var b = 2;
function foo() {
a++;
b = b * a;
a = b + 3;
}
function bar() {
b--;
a = 8 + b;
b = a * 2;
}
// ajax(..) is some arbitrary Ajax function given by a library
ajax( "http://some.url.1", foo );
ajax( "http://some.url.2", bar );”race condition, але — тільки два можливих результати
Які механізми існували в ядрі JS для роботи з асинхронними запитами до 2015 року?
Які механізми існували в ядрі JS для роботи з асинхронними запитами до 2015 року?
Частина перша.
ні обіцянок, ні пробачень
(pre-Promise JavaScript)
Якщо асинхронності немає, доведеться її винайти
Web API, events+callbacks
// ajax - some generic library for asynchronous calls
// based on events & callbacks
listen('click', function handler(evt) {
setTimeout(function request() {
ajax('http://some.url.1', function response(res) {
if (res.status === 200) {
handleSuccess(res)
} else {
handleFailure(res)
}
})
}, 500)
})
// ajax - some generic library for asynchronous calls
// based on events & callbacks
listen('click', handler)
function handler(evt) {
setTimeout(request, 500)
})
function request() {
ajax('http://some.url.1', response)
}
function response(res) {
if (res.status === 200) {
handleSuccess(res)
} else {
handleFailure(res)
}
}
doA(function() {
doB()
doC(function() {
doD()
})
doE()
})
doF()
doA(function() { // 1
doB() // 3
doC(function() { // 4
doD() // 6
})
doE() // 5
})
doF() // 2
analytics.trackPurchase(purchaseData, function() {
chargeCreditCard()
displayThankyouPage()
})
Trust issues
var tracked = false
analytics.trackPurchase(purchaseData, function() {
if (!tracked) {
tracked = true
chargeCreditCard()
displayThankyouPage()
}
})
Візуальна "pyramid of doom" — не єдина проблема колбеків:
- hardcoded handling для кожного окремого випадку (ad hoc)
- неочевидний порядок виконання (синхронність/асинхронність функцій впливає)
- вимушений Inversion of Control (ризик, що колбек не спрацює, або спрацює забагато разів)
Частина друга.
меньше всего нужны мне твои колбэки
(post-Promise JavaScript)
Promise API (ES2015+)
const result = new Promise((resolve, reject) => {
if (checkPasses) {
resolve('resolve value')
} else {
reject('rejection reason')
}
})
.then(
function fulfilled(value) {
return value
},
function rejected(reason) {
throw new Error(reason)
}
)
.catch(err => console.error(err))
Як визначити проміс?
p instanceof Promise // ❌ insufficientThenable type check
(duck typing 🦆)
function isThenable(value) {
return (
value !== null &&
typeof value === 'object' &&
typeof value.then === 'function'
)
}const pseudoPromise = {
then: function(cb, errcb) {
cb('Success!')
errcb('Error!')
}
}
pseudoPromise
.then(
function fulfilled(val) {
console.log(val) // "Success!"
},
function rejected(err) {
// WTF
console.log(err) // "Error!"
}
)

const pseudoPromise = {
then: function(cb, errcb) {
cb('Success!')
errcb('Error!')
}
}
Promise.resolve(pseudoPromise)
.then(
function fulfilled(val) {
console.log(val) // "Success!"
},
function rejected(err) {
// doesn't run
console.log(err) // "Error!"
}
)

Як проміс вирішує проблеми колбеків?
- Імутабельність - виконаний проміс лишається незмінним (Un-inversion of Control, колбек працює не більше одного разу)
- за рахунок .then порядок виконання зверху вниз
- Розширений інструментарій абстрактних методів для контролю flow (зменшує необхідність писати ad hoc код): Promise.resolve, Promise.reject, Promise.all, Promise.race, Promise.allSettled, Promise.any
Практика!
Async functions
const promise = async function() {
try {
const res = await fetch('some-url')
const data = await res.json()
return data
} catch(err) {
throw err
}
}function httpGet(url, responseType = '') {
return new Promise(function(resolve, reject) {
const request = new XMLHttpRequest()
request.onload = function() {
if (this.status === 200) {
// Success
resolve(this.response)
} else {
// Something went wrong (404 etc.)
reject(new Error(this.statusText))
}
}
request.onerror = function() {
reject(new Error('XMLHttpRequest Error: ' + this.statusText))
}
request.open('GET', url)
xhr.responseType = responseType
request.send()
})
}
Більше:
async-js
By Yevhen Orlov
async-js
- 100