Skip to content

Instantly share code, notes, and snippets.

@monochromer
Last active August 19, 2022 09:56
Show Gist options
  • Save monochromer/812cc71b319b1ae64c1c8e325bd5ad88 to your computer and use it in GitHub Desktop.
Save monochromer/812cc71b319b1ae64c1c8e325bd5ad88 to your computer and use it in GitHub Desktop.
Паттерны использования promise (Promise Patterns)
/*
conf: Falsy Values 2015
author: Kornel Lesinski
theme: And .then() what? Promise programming patterns
link: https://www.youtube.com/watch?v=KrhQE8K2I7Q
*/
// 1 waterfall. Использование результатов предыдущих промисов
doFirst()
.then(firstResult => {
return doSecond()
.then(seondResult => {
return doThird()
.then(thirdResult => f(firstResult, seondResult, thirdResult))
})
})
// 2 cache. Кеширование результатов асинхронных действий
const cache = new Map();
function work(input) {
if (!cache.has(input)) {
const promise = asyncWork(input);
cache.set(input, promise);
};
return cache.get(input);
}
// 3. serial queue. Последовательное выполнение промисов друг за другом
function serial(fnArray, onError, initialData) {
return fnArray
.reduce((p, f) => p.then(f), Promise.resolve(initialData))
.catch(onError);
}
// 4. Часто запрашиваемые данные одного и того же ресурса
let currentPromise = null;
function currentWeather() {
if (!currentPromise) {
currentPromise = fetch('/url.json')
.then(res => res.json())
.catch(err => console.log(err))
.then(() => currentPromise = null); // finally
};
return currentPromise;
}
// пример общей функции
function createBalanceLoadFunction(fn, onError) {
let currentPromise = null;
return function(...args) {
if (!currentPromise) {
currentPromise = fn(...args) // check if fn returns not promise
.catch(onError ? onError : console.error)
.finally(() => currentPromise = null);
};
return currentPromise;
}
}
// 5. ready event. Работа с событиями
let DOM = {
ready: new Promise(resolve => {
document.addEventListener('DOMContentLoaded', function onDOMContentLoaded() {
document.removeEventListener('DOMContentLoaded', onDOMContentLoaded);
resolve();
});
})
};
DOM.ready.then(doYourWork);
// example ===
/*
fetch(URL)
.then(res => res.json())
.then(data => Object.keys(data).map(key => data[key]))
.then(result => console.log(result))
.catch(error => console.error(`${error.message}, \n, ${error.stack}`))
*/
serial([
(url) => fetch(url),
res => res.json(),
data => Object.keys(data).map(key => data[key]),
result => console.log(result)
],
error => console.error(`${error.message}, \n, ${error.stack}`),
URL
)
// https://uxdesign.cc/crafting-beautiful-ux-with-api-requests-56e7dcc2f58e
// timeout
class TimeoutError extends Error {}
function promiseWithTimeout(p, timeout = 5000) {
return Primise.race([
p,
new Promise((resolve, reject) => {
setTimeout(reject, timeout, new TimeoutError(`It's timeout`))
})
])
}
// Minimum wait time
// it protects your app from fast API responses. A min wait is a great pattern to use if you want to show a loading state to the user, but the API might respond quickly. Users will end up seeing loading states and data “pop” into view before they can focus on anything.
// retry
// Отменяемые промисы
// https://noteskeeper.ru/1385/
// https://blog.bloomca.me/2017/12/04/how-to-cancel-your-promise.html
// Promise.allSettled
// https://github.com/jasonwilliams/proposal-promise-allSettled
// Promise.any
// Promise.some
/*
Promise.allSettled — это такой аналог Promise.all, который не реджектится если один из промисов зареджектился.
Я хотел бы предложить добавить еще два метода для работы с промисами — Promise.any и Promise.some.
Типичный юзкейс Promise.any — у нас есть несколько разных URL и мы хотим получить ответ от быстрейшего.
Может показаться, что для такой задачи подойдет Promise.race, но это не так.
Promise.race возвращает любой fulfilled промис, будь он resolved или reject.
Мы же хотим вернуть именно успешный, разрезолвленный промис и реджектнуть только если все промисы реджектнулись.
Promise.some принимает iterable первым параметром и число вторым.
Он возвращает массив разрезолвленных промисов указанной во втором параметре длины.
Если зареджектилось слишком много промисов (количество оставшихся промисов меньше, чем количество требуемых),
то Promise.some сразу же реджектится.
*/
function any(promises) {
return Promise.all(
promises.map(promise =>
promise.then(val => { throw val; }, _ => _),
),
).then(() => { throw Error('no successful connections'); }, _ => _);
}
// или (https://dev.to/vitalets/what-s-wrong-with-promise-allsettled-and-promise-any-5e6o)
const reverse = p => new Promise((resolve, reject) => Promise.resolve(p).then(reject, resolve));
Promise.any = arr => reverse(Promise.all(arr.map(reverse)));
// композиция асинхронных функций
// https://github.com/HowProgrammingWorks/AsyncCompose
//
function microtaskDebounce(fn) {
let called = false
return () => {
if (called) {
return
}
called = true
window.Promise.resolve().then(() => {
called = false
fn()
})
}
}
@emelyanov-dev
Copy link

24 строка, что за функция asyncWork?

@monochromer
Copy link
Author

24 строка, что за функция asyncWork?

Любая ваша функция, возвращающая результат асинхронной операции как promise

@myrecs
Copy link

myrecs commented Jun 22, 2022

promiseWithTimeout - тут ошибка, race не для этого. тут должкен быть all и 2 резолва

@monochromer
Copy link
Author

тут ошибка, race не для этого

в чём ошибка?

@myrecs
Copy link

myrecs commented Jun 23, 2022

тут ошибка, race не для этого

в чём ошибка?

В том что запустите ваш код и увидите что никакого таймаута нет. Race выдает первый выполненный промис.

@monochromer
Copy link
Author

Race выдает первый выполненный промис.

Race выдает promise, который первый поменял свое состояние. Если 2-ой промис завершится раньше, то мы должны поймать ошибку типа TimeoutError и отменить текущую операцию.

promiseWithTimeout(someAsyncOperation(), timeoutTime = 100)
  .then((result) => console.log(result))
  .catch((error) => {
    if (error instanceof TimeoutError) {
      console.log('need abort here async opertation')
      return
    }
    throw error
  })

@myrecs
Copy link

myrecs commented Jun 23, 2022

Race выдает promise, который первый поменял свое состояние

Мы с вами видимо про разные таймауты говорим. Вы сами привели ссылку над функцией в которой реализован таймаут. Так вот там таймаут именно в понимании обязательной задержки выполнения, а у вас таймаут про который вы щас пишите - отмена по истечению времени. При этом вы вырвали описание таймаута которое под фукцией с того сайта и оно неправильное в случае того что вы мне сейчас описываете! Перечитайте все сами!

@monochromer
Copy link
Author

там таймаут именно в понимании обязательной задержки выполнения

Там это pattern2 (Minimum wait time).
У меня в конспекте pattern1 (Timeouts). Вот он из статьи:

image

О терминологии. Минимальное ожидание - это скорее delay. Время, после которого операция должна оборваться - timeout. Именно так называется параметр в XMLHttpRequest.

@myrecs
Copy link

myrecs commented Jun 23, 2022

Минимальное ожидание - это скорее delay

Я согласен с тем что это скорее delay. Но у вас либо тут отсутсвует часть записи, либо просто нкпонятно к чему относится minimum wait time.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment