#javascript #callback #node-cron
#javascript #обратный вызов #узел-cron
Вопрос:
У меня возникли проблемы с доступом к методам класса без каких-либо изменений в самом классе.
Смотрите следующий демонстрационный код:
function myCallback() {
this.otherMethod();
}
class hiddenClass {
static hiddenFunction(callback) {
callback();
}
static otherMethod(){
console.log("success");
}
}
hiddenClass.hiddenFunction(myCallback);
В этом простом примере я хочу получить доступ к this.otherMethod() в моем обратном вызове. Очевидный ответ — изменить callback()
на callback.bind(this)()
, однако в этом случае hiddenClass будет библиотекой.
Как я могу вызвать этот другой метод?
Для справки, я пытаюсь создавать задания cron с использованием node-cron. Я хочу уничтожить эти задания, если проверка базы данных возвращает true, проверяя каждый цикл задания (в обратном вызове). Задание имеет внутренний метод .destroy()
. Я знаю, что могу сохранить задание cron в переменной, а затем вызвать variable.destroy()
, но есть ли способ сделать это в обратном вызове (поскольку эти задания создаются в цикле for, и я не хочу точно определять, какое из них уничтожить внутри обратного вызова)
cron.schedule(`5 * * * * *`, async function () {
schedule = await Schedule.findById(schedule._id);
if (!schedule) this.destroy() // Destroy the job if schedule is not found (this returns Window)
}
);
Ответ №1:
Ваши рассуждения верны, вам нужно будет привязать обратный вызов к рассматриваемому классу. Однако это можно было бы сделать в вашем примере кода, изменив вызов hiddenFunction:
hiddenClass.hiddenFunction(myCallback.bind(hiddenClass));
Это не потребует внесения изменений в библиотеку.
Что касается вашего ссылочного кода…
До ES2015 способ сделать это заключался бы в создании анонимных функций, связанных вызовом со значением текущей итерации цикла, для простого примера:
var schedules = [1,2,3,4,5];
for(var i=0;i<schedules.length;i ){
setTimeout(function(){console.log(schedules[i])}, 10);
}
/* => outputs:
5
5
5
5
5
*/
for(var i=0;i<schedules.length;i ){
// create an anonymous function with the first arg bound at call time
(function(j){
setTimeout(function(){console.log(schedules[j])}, 10);
}(i));
}
/* => outputs:
1
2
3
4
5
*/
ОДНАКО в вашем коде это было бы невозможно, поскольку вы пытаетесь передать анонимный обратный вызов и ссылаться на значение итерации текущего цикла в указанной функции, требуя, чтобы анонимная функция знала возвращаемое значение cron.schedule
перед передачей в cron.schedule
.
Решение проблемы заключается в использовании let
или const
в вашем цикле для привязки значения текущей итерации. Они ограничены областью блока, а не лексической областью, поэтому они сохраняют значение на каждой итерации, что приводит к N закрытиям одной и той же анонимной функции. Мы можем продемонстрировать разницу между ними в следующем цикле:
for(var foo of [1,2,3,4,5]){
const bar = foo;
(async function(){
await Promise.resolve().then(()=>console.log({bar, foo}));
}());
}
/* outputs:
{ bar: 1, foo: 5 }
{ bar: 2, foo: 5 }
{ bar: 3, foo: 5 }
{ bar: 4, foo: 5 }
{ bar: 5, foo: 5 }
*/
Связывая все это вместе, ваш планировщик циклов будет выглядеть следующим образом:
for(let schedule of schedules){
const me = cron.schedule(`5 * * * * *`, async function () {
// the value of `me` is bound to each iteration and you can refer to it as needed
schedule = await Schedule.findById(schedule._id);
if (!schedule) me.destroy();
});
}
Комментарии:
1. замечательно, что работает, спасибо! Я действительно думал об этом решении, но я бы подумал,
me
что оно не определено. Моя логика заключается в том, что переменная назначается только после запуска функции cron, почему она назначается в этом случае?2. Переменная назначается во время
cron.schedule
вызова, и обратный вызов обязательно всегда вызывается после этого времени.