Асинхронный код

Очередь событий. Таймеры. Callback. Promise

Жигалов Сергей

Синхронный код

Синхронный код


function grist() {
   return 'Перемолоть кофейные зерна';
}

function addWater() {
   return 'Добавить немного воды';
}

function toStove() {
   return 'Поставить на плиту';
}
        

Синхронный код


grist();
addWater();
toStove();
        

Синхронный код


grist();     // 'Перемолоть кофейные зерна'
addWater();
toStove();
        

Синхронный код


grist();     // 'Перемолоть кофейные зерна'
addWater();  // 'Добавить немного воды'
toStove();
        

Синхронный код


grist();     // 'Перемолоть кофейные зерна'
addWater();  // 'Добавить немного воды'
toStove();   // 'Поставить на плиту'
        

Стек вызовов

Стек вызовов

function prepareCoffee() {
    toStove();
}

function toCheerUp() {
    prepareCoffee();
}

        








anonymous

Стек вызовов

function prepareCoffee() {
    toStove();
}

function toCheerUp() {
    prepareCoffee();
}

toCheerUp();







toCheerUp
anonymous

Стек вызовов

function prepareCoffee() {
    toStove();
}

function toCheerUp() {
    prepareCoffee();
}

toCheerUp();






prepareCoffee
toCheerUp
anonymous

Стек вызовов

function prepareCoffee() {
    toStove();
}

function toCheerUp() {
    prepareCoffee();
}

toCheerUp();





toStove
prepareCoffee
toCheerUp
anonymous

Стек вызовов

function prepareCoffee() {
    toStove();
}

function toCheerUp() {
    prepareCoffee();
}

toCheerUp();






prepareCoffee
toCheerUp
anonymous

Стек вызовов

function prepareCoffee() {
    toStove();
}

function toCheerUp() {
    prepareCoffee();
}

toCheerUp();







toCheerUp
anonymous

Стек вызовов

function prepareCoffee() {
    toStove();
}

function toCheerUp() {
    prepareCoffee();
}

toCheerUp();








anonymous

Стек вызовов

function prepareCoffee() {
    toStove();
}

function toCheerUp() {
    prepareCoffee();
}

toCheerUp();








        

Стек вызовов


function toStove() {
    throw new Error('No electricity');
}
function prepareCoffee() {
    toStove();
}
function toCheerUp() {
    prepareCoffee();
}

toCheerUp();
        

Uncaught Error: No electricity
    at toStove (<anonymous>:2:11)
    at prepareCoffee (<anonymous>:6:5)
    at toCheerUp (<anonymous>:10:5)
    at <anonymous>:13:1
        

Очередь событий

Очередь событий

function prepareCoffee() {
    toStove();
}

function toCheerUp() {
    prepareCoffee();
}

toCheerUp();








        
anonymous

Очередь событий

function prepareCoffee() {
    toStove();
}

function toCheerUp() {
    prepareCoffee();
}

toCheerUp();








anonymous
 

Очередь событий

function prepareCoffee() {
    toStove();
}

function toCheerUp() {
    prepareCoffee();
}

toCheerUp();







toCheerUp
anonymous

Очередь событий

function prepareCoffee() {
    toStove();
}

function toCheerUp() {
    prepareCoffee();
}

toCheerUp();






prepareCoffee
toCheerUp
anonymous

Очередь событий

function prepareCoffee() {
    toStove();
}

function toCheerUp() {
    prepareCoffee();
}

toCheerUp();





toStove
prepareCoffee
toCheerUp
anonymous

Очередь событий

function prepareCoffee() {
    toStove();
}

function toCheerUp() {
    prepareCoffee();
}

toCheerUp();






prepareCoffee
toCheerUp
anonymous

Очередь событий

function prepareCoffee() {
    toStove();
}

function toCheerUp() {
    prepareCoffee();
}

toCheerUp();







toCheerUp
anonymous

Очередь событий

function prepareCoffee() {
    toStove();
}

function toCheerUp() {
    prepareCoffee();
}

toCheerUp();








anonymous

Очередь событий

function prepareCoffee() {
    toStove();
}

function toCheerUp() {
    prepareCoffee();
}

toCheerUp();








        

Системный таймер

Сальвадор Дали «Постоянство времени»

setTimeout

setTimeout(func[, delay, param1, param2, ...]);

function toStove() {
   return 'Поставить на плиту';
}

function fromStove() {
   return 'Снять с плиты';
}

toStove();
setTimeout(fromStove, 5000);
        

function toStove() {
   return 'Поставить на плиту';
}

function fromStove() {
   return 'Снять с плиты';
}

toStove();
setTimeout(fromStove, 5000);
        

anonymous
        

        

function toStove() {
   return 'Поставить на плиту';
}

function fromStove() {
   return 'Снять с плиты';
}

toStove();
setTimeout(fromStove, 5000);
        
 
'Поставить на плиту'
        
0 sec

function toStove() {
   return 'Поставить на плиту';
}

function fromStove() {
   return 'Снять с плиты';
}

toStove();
setTimeout(fromStove, 5000);
        
 
'Поставить на плиту'
        
5 sec

function toStove() {
   return 'Поставить на плиту';
}

function fromStove() {
   return 'Снять с плиты';
}

toStove();
setTimeout(fromStove, 5000);
        
 
'Поставить на плиту'
        

function toStove() {
   return 'Поставить на плиту';
}

function fromStove() {
   return 'Снять с плиты';
}

toStove();
setTimeout(fromStove, 5000);
        
fromStove
'Поставить на плиту'
        

function toStove() {
   return 'Поставить на плиту';
}

function fromStove() {
   return 'Снять с плиты';
}

toStove();
setTimeout(fromStove, 5000);
        
 
'Поставить на плиту'
'Снять с плиты'

setInterval

setInterval(func[, delay, param1, param2, ...]);

function toStove() {
   return 'Поставить на плиту';
}
function toStir() {
   return 'Помешивать';
}

toStove();
setInterval(toStir, 1000);
        

function toStove() {
   return 'Поставить на плиту';
}
function toStir() {
   return 'Помешивать';
}

toStove();
setInterval(toStir, 1000);
        
anonymous



        

function toStove() {
   return 'Поставить на плиту';
}
function toStir() {
   return 'Помешивать';
}

toStove();
setInterval(toStir, 1000);
        
 
'Поставить на плиту'


        
0 sec

function toStove() {
   return 'Поставить на плиту';
}
function toStir() {
   return 'Помешивать';
}

toStove();
setInterval(toStir, 1000);
        
 
'Поставить на плиту'


        
1 sec

function toStove() {
   return 'Поставить на плиту';
}
function toStir() {
   return 'Помешивать';
}

toStove();
setInterval(toStir, 1000);
        
 
'Поставить на плиту'


        
1 sec

function toStove() {
   return 'Поставить на плиту';
}
function toStir() {
   return 'Помешивать';
}

toStove();
setInterval(toStir, 1000);
        
fromStove
'Поставить на плиту'
'Помешивать'

        
2 sec

function toStove() {
   return 'Поставить на плиту';
}
function toStir() {
   return 'Помешивать';
}

toStove();
setInterval(toStir, 1000);
        
 
'Поставить на плиту'
'Помешивать'

        
2 sec

function toStove() {
   return 'Поставить на плиту';
}
function toStir() {
   return 'Помешивать';
}

toStove();
setInterval(toStir, 1000);
        
fromStove
'Поставить на плиту'
'Помешивать'
'Помешивать'
        
3 sec

function toStove() {
   return 'Поставить на плиту';
}
function toStir() {
   return 'Помешивать';
}

toStove();
setInterval(toStir, 1000);
        
 
'Поставить на плиту'
'Помешивать'
'Помешивать'
        
3 sec

function toStove() {
   return 'Поставить на плиту';
}
function toStir() {
   return 'Помешивать';
}

toStove();
setInterval(toStir, 1000);
        
fromStove
'Поставить на плиту'
'Помешивать'
'Помешивать'
'Помешивать'
4 sec

function toStove() {
   return 'Поставить на плиту';
}
function toStir() {
   return 'Помешивать';
}

toStove();
setInterval(toStir, 1000);
        
 
'Поставить на плиту'
'Помешивать'
'Помешивать'
'Помешивать'

var id = setInterval(fromStove, 1000);
        

clearInterval(id);
        

function toStir() {
   return 'Помешивать';
}

console.log('Поставить на плиту');
setInterval(toStir, 1000);
executionThreeSeconrs();
        

function toStir() {
   return 'Помешивать';
}

console.log('Поставить на плиту');
setInterval(toStir, 1000);
executionThreeSeconrs();
        
anonymous



        

function toStir() {
   return 'Помешивать';
}

console.log('Поставить на плиту');
setInterval(toStir, 1000);
executionThreeSeconrs();
        
 
'Поставить на плиту'


        
0 sec

function toStir() {
   return 'Помешивать';
}

console.log('Поставить на плиту');
setInterval(toStir, 1000);
executionThreeSeconrs();
        
 
'Поставить на плиту'


        
0 sec

function toStir() {
   return 'Помешивать';
}

console.log('Поставить на плиту');
setInterval(toStir, 1000);
executionThreeSeconrs();
        
 
'Поставить на плиту'


        
1 sec

function toStir() {
   return 'Помешивать';
}

console.log('Поставить на плиту');
setInterval(toStir, 1000);
executionThreeSeconrs();
        
fromStove
'Поставить на плиту'


        
2 sec

function toStir() {
   return 'Помешивать';
}

console.log('Поставить на плиту');
setInterval(toStir, 1000);
executionThreeSeconrs();
        
fromStove fromStove
'Поставить на плиту'


        
3 sec

function toStir() {
   return 'Помешивать';
}

console.log('Поставить на плиту');
setInterval(toStir, 1000);
executionThreeSeconrs();
        
fromStove fromStove fromStove
'Поставить на плиту'


        
3.001 sec

function toStir() {
   return 'Помешивать';
}

console.log('Поставить на плиту');
setInterval(toStir, 1000);
executionThreeSeconrs();
        
 
'Поставить на плиту'
'Помешивать'
'Помешивать'
'Помешивать'

Выводы

  • delay гарантирует, что событие произойдёт не раньше указанного времени
  • setInterval Vs setTimeout

setInterval

setInterval

setInterval → setTimeout


function toStir() {
    console.log('Помешивать');

    setTimeout(toStir, 1000);
};

toStir();
        

setInterval → setTimeout

setTimeout

Нулевой интервал


function beHappy() {
    return 'Насладиться результатом';
}

setTimeout(beHappy, 0);

console.log('Много-много синхронных дел');
        

function beHappy() {
    return 'Насладиться результатом';
}

setTimeout(beHappy, 0);

console.log('Много-много синхронных дел');
        
anonymous

        

function beHappy() {
    return 'Насладиться результатом';
}

setTimeout(beHappy, 0);

console.log('Много-много синхронных дел');
        
beHappy

        

function beHappy() {
    return 'Насладиться результатом';
}

setTimeout(beHappy, 0);

console.log('Много-много синхронных дел');
        
beHappy
'Много-много синхронных дел'
        

function beHappy() {
    return 'Насладиться результатом';
}

setTimeout(beHappy, 0);

console.log('Много-много синхронных дел');
        
beHappy
'Много-много синхронных дел'
'Насладиться результатом'

Работа с файлами


var fs = require('fs');
var fileName = __dirname + '/data.json';

var data = fs.readFileSync(fileName, 'utf-8');

console.log(data);
        

'{"name": "Sergey"}'
        

var fs = require('fs');
var fileName = __dirname + '/data.json';

console.time('readFileSync');
var data = fs.readFileSync(fileName, 'utf-8');
console.timeEnd('readFileSync');

console.log(data);
        

readFileSync: 3ms
'{"name": "Sergey"}'
        

var fs = require('fs');
var fileName = __dirname + '/bigData.mov';

console.time('readFileSync');
var data = fs.readFileSync(fileName, 'utf-8');
console.timeEnd('readFileSync');

console.log(data);
        

readFileSync: 3567ms
<много текста>
        

Во время синхронных операций не обрабатываются другие события: таймеры, пользовательские события и тп

Пример

Some action

var fs = require('fs');
var fileName = __dirname + '/data.json';

var data = fs.readFile(fileName, 'utf-8');

console.log(data);
        

undefined
        

var fs = require('fs');
var fileName = __dirname + '/data.json';

fs.readFile(fileName, 'utf-8', function (err, data) {
    console.log(data);
});
        

'{"name": "Sergey"}'
        

callback

function cb(err, data) {





}

callback

function cb(err, data) {
    if (err) {
        console.error(err.stack);
    }


}

callback

function cb(err, data) {
    if (err) {
        console.error(err.stack);
    } else {
        console.log(data);
    }
}

callback. Достоинства

  • Нет накладных расходов
  • Не нужно подключать дополнительные библиотеки

callback. Недостатки

  • Глубокий уровень вложенности

var fs = require('fs');

fs.readFile('data.json', function (err, data) {
    if (err) {
        console.error(err.stack);
    } else {
        console.log(data);
    }
});
        

var fs = require('fs');

fs.readFile('data.json', function (err, data) {
    if (err) {
        console.error(err.stack);
    } else {
        fs.readFile('ext.json', function (e, ext) {
            if (e) {
                console.error(e.stack);
            } else {
                console.log(data + ext);
            }
        });
    }
});
        

callback. Недостатки

  • Глубокий уровень вложенности
  • Обработка ошибок и данных в одном месте
  • Необработанные исключения

var fs = require('fs');

function readTwoFiles(cb) {
    var tmp;

    fs.readFile('data.json', function (err, data) {
        if (tmp) {cb(err, data + tmp);}
        else { tmp = data; }
    });

    fs.readFile('ext.json', function (err, data) {
        if (tmp) {cb(err, tmp + data);}
        else { tmp = data; }
    });
}
        

var fs = require('fs');

function readTwoFiles(cb) {
    var tmp;

    fs.readFile('data.json', function (err, data) {
        if (tmp) {cb(err, data + tmp);}
        else { throw Error('Mu-ha-ha!'); }
    });

    fs.readFile('ext.json', function (err, data) {
        if (tmp) {cb(err, tmp + data);}
        else { tmp = data; }
    });
}
        

callback. Недостатки

  • Глубокий уровень вложенности
  • Обработка ошибок и данных в одном месте
  • Необработанные исключения
  • Лишние переменные

callback. Когда использовать

  • Нужна выcокая производительность
  • Код библиотеки
async.js https://github.com/caolan/async

function readTwoFiles(cb) {
    async.parallel([
        fs.readFile.bind(fs, 'data.json'),
        fs.readFile.bind(fs, 'ext.json')
    ], function (err, data) {
        cb(err, data[0] + data[1])
    });
}
        

Promises


fs.readFile('data.json', function (err, data) {
    if (err) {
        console.error(err);
    } else {
        console.log(data);
    }
});
        

Асинхронный код


fs.readFile('data.json', function (err, data) {
    if (err) {
        console.error(err);
    } else {
        console.log(data);
    }
});
        

Обработчики


fs.readFile('data.json', function (err, data) {
    if (err) {
        console.error(err);
    } else {
        console.log(data);
    }
});
        

Promises. Асинхронный код


var promise = new Promise(function (resolve, reject) {







});
        

Promises. Асинхронный код


var promise = new Promise(function (resolve, reject) {
    fs.readFile('data.json', function (err, data) {





    });
});
        

Promises. Асинхронный код


var promise = new Promise(function (resolve, reject) {
    fs.readFile('data.json', function (err, data) {
        if (err) {
            reject(err);
        }


    });
});
        

Promises. Асинхронный код


var promise = new Promise(function (resolve, reject) {
    fs.readFile('data.json', function (err, data) {
        if (err) {
            reject(err);
        } else {
            resolve(data);
        }
    });
});
        
Promise

Promises. Обработчики

promise.then();



        

Promises. Обработчики

promise.then(function (data) {
        console.log(data)
    });

        

Promises. Обработчики


promise.then(function (data) {
        console.log(data)
    }, function (err) {
        console.error(err);
    });
        

Promises. Обработчики


promise.then(console.log, console.error);
        

Promises. Недостатки

  • Дополнительные обёртки
  • Немного медленнее, чем callback

Promises. Достоинства

  • Контролируем исключения

var promise = new Promise(function (resolve, reject) {
    fs.readFile('data.json', function (err, data) {
        if (err) {
            reject(err);
        } else {
            throw new Error('Mu-ha-ha!');
        }
    });
});
        

promise.then(console.log, console.error);
        

[Error: Mu-ha-ha!]
        

Promises. Достоинства

  • Контролируем исключения
  • Несколько обработчиков

Чейнинг

Promise

function identity(data) {
    return data;
}

function thrower(err) {
    throw err;
}
        

promise
    .then(console.log, thrower)
    .then(identity, console.error);
        
promise simple

promise
    .then(JSON.parse, thrower)
    .then(identity, getDefault)
    .then(getAvatar, thrower)
    .then(identity, console.error);
        

function getDefault() {
    return { name: 'Sergey' };
}

function getAvatar (data) {
    var name = data.name;
    return request('https://my.avatar/' + name);
}
        
promise complex

promise
    .then(JSON.parse, thrower)
    .then(identity, getDefault)
    .then(getAvatar, thrower)
    .then(identity, console.error);
        

promise
    .then(JSON.parse, thrower)
    .then(identity, getDefault)
    .then(getAvatar, thrower)
    .then(identity, console.error);
        

promise
    .then(JSON.parse)
    .catch(getDefault)
    .then(getAvatar)
    .catch(console.error);
        

promise
    .then(JSON.parse)
    .then(getAvatar);
        

не то же самое, что


promise.then(JSON.parse);
promise.then(getAvatar);
        
  • then вернул данные — передаём по цепочке дальше
  • then вернул промис — передаём результат работы промиса
  • произошла ошибка — реджектим промис

Promise.all


function readFile(name) {
    return new Promise(function (resolve, reject) {
        fs.readFile(name, function (err, data) {
            err ? reject(err) : resolve(data);
        });
    });
}
        

Promise.all


Promise
    .all([
        readFile('data.json'),
        readFile('ext.json')
    ])
    .then(function (data) {
        console.log(data[0] + data[1])
    });
        

Promise
    .resolve('{"name": "Sergey"}')
    .then(console.log);
        

'{"name": "Sergey"}'
        

Promise
    .reject(new Error('Mu-ha-ha!'))
    .catch(console.error);
        

[Error: Mu-ha-ha!]
        

Почитать