Как написать простой, понятный, легко обслуживаемый код, который запускает последовательно несколько асинхронных функций в javascript/jQuery? (когда отработает одна, должна запускаться другая)
Следующий пример иллюстриует мой вопрос:
function f1(){ setTimeout( function(){ console.log(1); }, 30); }
function f2(){ setTimeout( function(){ console.log(2); }, 20); }
function f3(){ setTimeout( function(){ console.log(3); }, 10); }
f1(); f2(); f3();
на выходе 3 2 1 как сделать что-бы выдавало 1 2 3 ?
Желательно без коллбеков - т.к. если надо запустить последовательно больше двух функций это уже тяжело читать. Многое говорит о том что возможно решение с помощью обьекта $.Deferred, но пока не видел разумного варианта.
Подобный вопрос задавался не раз, но я почему-то не нашел ответа который бы меня устроил.
Если вы хотите использовать Обещания (Promise), то для начала вам нужно модифицировать ваши функции так, чтобы они возвращали Обещания. Например, первая из ваших функций будет иметь вид:
function f1() {
return new Promise(function(resolve){
setTimeout(function() {
console.log(1);
resolve();
}, 30);
});
}
Остальные функции преобразуются аналогичным образом.
Теперь у вас есть три функции (f1
, f2
, f3
), которые возвращают Обещания и вы хотите выполнить их последовательно. Если вы не используете библиотек, вроде Bluebird, то вам придется реализовать очередь вызова Обещаний вручную. Это не так сложно, как кажется:
// Аргумент "deeds" - это массив функций, которые должны выполняться
// последовательно. При этом, каждая функция должна возвращать
// Обещание (Promise).
var seqRunner = function(deeds) {
return deeds.reduce(function(p, deed) {
return p.then(function() {
// Выполняем следующую функцию только после того, как отработала
// предыдущая.
return deed();
});
}, Promise.resolve()); // Инициализируем очередь выполнения.
}
А пользоваться этой очередью нужно вот так:
seqRunner([f1, f2, f3]).then(function() {
console.log('Done!');
});
А вот и JSFiddle с рабочим примером.
Замечание:
Если у вас заранее известное, небольшое число функций, то можно вообще обойтись без функции seqRunner
и связывать функции вручную:
f1().then(function() {
return f2();
}).then(function() {
return f3();
}).then(function() {
console.log('Done!');
});
Альтернативный вариант, сделать обертку над setTimeout
возвращающую Promise
, например так:
function delay(timeout){
return new Promise(function(r){
setTimeout(r,timeout);
});
}
Теперь код из вопроса может выглядеть следующим образом:
function delay(timeout) {
return new Promise(function(r) {
setTimeout(r, timeout);
});
}
function f1() {
console.log(1);
}
function f2() {
console.log(2);
}
function f3() {
console.log(3);
}
delay(3000).then(f1)
.then(function(){return delay(2000);})
.then(f2)
.then(function(){ return delay(1000);})
.then(f3)
.then(function() {
console.log('all finish');
});
Или даже так
function delay(timeout) {
return new Promise(function(r) {
setTimeout(r, timeout);
});
}
function f1() {
console.log(1);
return 2000
}
function f2() {
console.log(2);
return 1000;
}
function f3() {
console.log(3);
}
delay(3000).then(f1)
.then(delay)
.then(f2)
.then(delay)
.then(f3)
.then(function() {
console.log('all finish');
});
Если таких функций много, то можно собрать из них массив и с помощью функции reduce собрать один большой Promise
function delay(timeout) {
return new Promise(function(r) {
setTimeout(r, timeout);
});
}
function f1() {
console.log(1);
return 2000
}
function f2() {
console.log(2);
return 1000;
}
function f3() {
console.log(3);
}
[f1, f2, f3].reduce(function(promise, func) {
return promise.then(func).then(delay);
}, delay(3000))
.then(function() {
console.log('all finish')
});
Если каждой добавить таки callback отчёт о выполнении, можно держать очередь в массиве и вызывать следующую ф-ю по завершению предыдущей:
function f1(){ setTimeout( function(){ console.log(1); next(); }, 30); }
function f2(){ setTimeout( function(){ console.log(2); next(); }, 20); }
function f3(){ setTimeout( function(){ console.log(3); next(); }, 10); }
var queue = [f1, f2, f3]
,i=0
,next = function(){ queue[i] && queue[i++]();}
;
next(); // 1 2 3
Виртуальный выделенный сервер (VDS) становится отличным выбором
И так уважаемые эксперты, есть сайт ajax, но котором есть 10 одинаковых страниц, но с разным выводом инфы из бд, и каждый раз требуется выполнять...
Можете помочь, пожалуйстаПерехожу с JS На TS, и сразу встал в ступор
начинаю изучать javascript, пишу скрипт, с помощью которого меняется цвет текста по клику мышиПочему-то не работает данный код, консоль собственно...
Такая задача: у меня есть блок и при попадании в видимость экрана на него вешается классview