четверг, 12 ноября 2020 г.

A programmer: JavaScript #21. Event loop + Promises

JavaScript - однопоточный язык, то есть у него один стек, следовательно, только одна комманда может быть выполнена в единицу времени. Но если у тебя только один поток, как у JavaScript, это может привести к серьёзным задержкам. Проблема с однопоточностью решается "асинхронностью". То есть JavaScript использует дополнительные ресурсы браузера или NodeJS или какого-либо другого окружения, кроме собственно основного движка, для того, чтобы иметь возможность что-то делегировать этим ресурсам и не ждать окончания одной операции, чтобы приступить к следующей.

Это реализуется через "цикл событий" ("event loop"):

  1. Когда функция вызывается, она загружается в стек.
  2. Но потом она начинает выполняться в "дополнительных ресурсах" пока не выполнится до конца.
  3. Пока она выполняется "в стороне", другие функции из стека могут быть в него загружены.
  4. Когда функция выполнена, она возвращается сначала в "очередь на возвращение" ("task queue") и обратно в стек.

Event loop - потому что JavaScript циклом мечется между стеком, и "очередью", проверяя, 1) пустой ли стек, и если стек пустой, то 2) есть ли в очереди то, что нужно возвращать. И если ещё нету, бежит дальше по кругу, если есть - помещает это (то, что первое в очереди) в стек.

И здесь есть большая проблема: часто мы не можем знать, в какой очередности будут выполняться функции. То есть что выполнится первым - то, что идет следующим в коде или то, что вернется из очереди?

Так вот, чтобы избежать синхронности придумали колбеки ("callback"). Колбек - это функция, которая передается в качестве параметра в другую функцию, если нужно, чтобы она была выполнена после той, в которую передана (или в нужном месте внутри). Например:

function callback() {

    console.log('I am callback!');

};

let a = 'I am a parameter of anotherFunction!';

function anotherFunction(a, callback) {

    console.log(a);

    callback();

}

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

Чтобы его избежать, придумали (например) промисы ("promises").

Промис - это объект, который "обещает" выполнить то, что внутри. Он принимает функцию, которая имеет параметры resolve и reject, которые, соответственно, вызываются в случае успешного или неуспешного выполнения (вызываются с необходимыми параметрами). То есть, промис не только имеет возвращаемое значение той функции, которая выполнялась внутри него, но и состояние, успешно или нет прошло выполнение. И это состояние можно использовать - в случае неуспешного выполнения - ловить ошибку, добавив к промису через точку catch(...), а в случае успешного - вызывать следующую функцию, добавив к промису через точку then(...). Ну и соответственно, можно промисы цеплять в цепочку один за одним, если принципиально последовательное выполнение определенных функций.

И поскольку я вчера тупил, и не смог написать промис на собеседовании, то вот пример:

let promise = new Promise(function(resolve, request) {

    let data = 5 + 3;

    resolve(data);

}).then(function(data) {

     console log(data + 125);

});

Как-то так.

Комментариев нет: