У меня есть сложная функция обещания, которая вызывается несколько раз с одними и теми же параметрами. Я хочу, чтобы избежать повторного запуска одних и тех же вычислений для одних и тех же параметров. Вместо этого, если последовательный вызов имеет те же параметры, вернуть обещание, созданное предыдущим.
Обычно для таких случаев я бы использовал пакет memoizee (или memoize в lodash). Однако это обещание возвращает большой набор данных, который может быть проблематично хранить в памяти. Поскольку эти пакеты кэшируют результаты в памяти, они не подходят для этого случая.
Упрощенный пример:
const sleep = ms => new Promise(resolve => setTimeout(resolve, ms))
async function sleepOneSec() {
console.info("going to sleep");
await sleep(1000)
};
async function myApp() {
await Promise.all([1, 2, 3].map(async item => {
console.info('item', item);
await sleepOneSec();
}));
console.info('promise.all is done');
await sleepOneSec();
console.info('completed');
}
myApp().then(() => {}).catch(err => console.info(err));
в настоящее время без какого-либо запоминания вывод:
item 1
going to sleep
item 2
going to sleep
item 3
going to sleep
promise.all is done
going to sleep
completed
В идеале я хочу, чтобы результаты были:
item 1
going to sleep
item 2
item 3
promise.all is done
going to sleep
completed
Где функция с аргументами, где вы хотите объединить обещание, если аргументы одинаковы? Единственные функции, возвращающие обещания, которые вы показываете, — это sleep() и sleepOneSec(). Пожалуйста, покажите свой реальный код, а не какой-то вымышленный код, чтобы мы могли решить реальные проблемы, которые могут у вас возникнуть. Нам также необходимо знать, к какому типу данных относятся аргументы и, если они являются объектами, как именно их следует сравнивать на равенство.
@derpirscher, спасибо, но это не поможет. Если я сохраняю результат обещания в этой коллекции, то практически я кэширую его в памяти, чего мне не нужно. Запись в файл занимает много времени и требует сериализации, что в моем случае очень сложно и нереалистично.
@ jfriend00, настоящий код здесь слишком длинный, никто не станет его читать. Это на самом деле не очень важно. Если у вас есть решение, которое не обрабатывает параметры, это было бы здорово. Если параметры важны, вы можете заменить sleepOneSec() на sleepOneSec(num), который возвращает полученное число. Все вызовы этой функции передают один и тот же параметр: «1».
Так что в основном вам не нужны ваши результаты в памяти, и вы не можете записать их в файл... У меня закончились идеи, где еще их хранить...



![Безумие обратных вызовов в javascript [JS]](https://i.imgur.com/WsjO6zJb.png)


Хорошо, вот реализация для sleepOneSec(num), которая генерирует требуемый результат. Он создает кеш для промиса от sleepOneSec(), который индексируется аргументами. Обещание удаляется из кеша, как только оно разрешается или отклоняется, поэтому оно повторно используется только во время «выполнения»:
const sleep = ms => new Promise(resolve => setTimeout(resolve, ms))
const sleepCache = new Map();
async function sleepOneSec(num) {
// serialize args
const args = JSON.stringify({ num });
let p = sleepCache.get(args);
if (!p) {
console.info("going to sleep");
p = sleep(num);
sleepCache.set(args, p);
// when this promise resolves, remove it from the cache
// so it won't be used any more
const remove = () => sleepCache.delete(args);
p.then(remove, remove);
}
return p;
}
async function myApp() {
await Promise.all([1, 2, 3].map(async item => {
console.info('item', item);
await sleepOneSec(1000);
}));
console.info('promise.all is done');
await sleepOneSec(1000);
console.info('completed');
}
myApp().then(() => {}).catch(err => console.info(err));Если аргументы рассматриваемой функции содержат объекты, то вы должны убедиться, что они однозначно сериализуемы с помощью JSON.stringify(), или вам нужно вручную преобразовать их во что-то, что представляет их уникальность и сериализуемо с помощью JSON.stringify(). Также важно вручную обрабатывать сериализацию любых объектов, потому что JSON.stringify() не обязательно приводит свойства в строку в том же порядке, который нарушит сравнение. Вот почему я спросил об аргументах в вашем реальном коде. Все, что вы предложили мне, когда я спросил, каковы настоящие аргументы, это один числовой аргумент, вот что показано здесь. Но если у вас есть объекты в качестве аргументов, с ними нужно обращаться осторожно, чтобы кэширование работало правильно.
Фантастический! Именно то, что я искал. Большое спасибо за подробный ответ! Я не уверен, что здесь второе remove: p.then(remove, remove); Думаю, это может быть: p.then(remove);. Что касается параметров, я полностью понимаю (я делал это много раз раньше, поэтому не хотел усложнять здесь). Еще раз спасибо! :)
@David - promise.then(resolveHandler, rejectHandler) принимает два аргумента (второй необязателен). Если предоставлен, второй аргумент — это rejectHandler. Вы должны убедиться, что вы удалили обещание из кеша, когда обещание разрешено ИЛИ отклонено.
О, это верно. Я привык к синтаксису .then().catch(). Спасибо за разъяснение.
Сохраните промис в какой-нибудь коллекции, где его можно найти через параметры. Прежде чем создавать новый промис с набором параметров, посмотрите в своем хранилище, нет ли уже промиса с этими параметрами. Если да, верните его, если нет создайте новый. Конечно, это сохранит результат обещания в памяти. Если вы этого не хотите, запишите результат в файл после разрешения промиса и верните
readFileили аналогичный для последующих вызовов.