Translate

Поиск по этому блогу

пятница, 28 сентября 2018 г.

ES6: Символы (symbols) (XV).

Symbol

Перед тем. как перейти к подробному изучению нового типа данных в ES6, стоит разобраться зачем они нужны.

Symbol - специальный тип данных с помощью которого можно создать уникальный идентификатор свойств объектов. Именно для этого и придумали символы.



Все материалы по ES6


Символы - уникальные и неизменные значения.

Символы позволяют разработчикам добавлять новые идентификаторы или свойствам объектов, при этом не резервируя строковые названия для этих свойств.

В основном символы используются внутри языка JavaScript и вам вряд ли придется часто с ними работать.

Давайте перейдем в редактор и посмотрим как создаются и используются символы.

Символы поддерживаются всеми современными браузерами и последними версиями ноды. Для поддержки старых версий на сайте Babel

Пройти по пути сайт Babel -> Usage ->Polyfill



Установить через npm в папку проекта и подключить в файле index.html

<script src="node_modules/babel-polyfill/dist/polyfill.min.js"></script>

Откроем в редакторе файл symbol.js, подключим его к нашему файлу index.html, точно так же, как мы это делали в предыдущих частях руководства.

<script src="src/symbol.js"></script>

Создание символов

Создадим переменную и в нее пометим результат вызова функции Symbol.

Результат выведем в консоль.


let symbol = Symbol();

console.log(symbol);



Посмотрим в консоль



Давайте посмотрим на тип переменной symbol используя оператор typeof.


let symbol = Symbol();

console.log( typeof symbol);





Заметьте, что при создании нового символа мы не используем слово new.

Если мы, все таки попробуем, то получим ошибку.


let symbol = new Symbol();

console.log( typeof symbol);





Это происходит потому, что Symbol не является конструктором.

У функции Symbol() есть необязательный аргумент, с помощью которого мы можем указать имя символа, что может быть полезно при отладке.


let symbol = Symbol('name');

console.log(symbol);





Давайте попробуем создать еще один символ и присвоить ему такое же имя и посмотрим, будет ли равняться один другому.


let symbol = Symbol('name');
let symbol2 = Symbol('name');

console.log(symbol);
console.log(symbol2);





При создании символов мы увидели одинаковые сообщения. но при этом символы не равны!

Каждый символ уникален. Это можно представить так. что внутри символа хранится некий уникальный код.

Таким образом имя символа мы используем для обращения к нему.

Создать два одинаковых символа нельзя!

Посмотреть на внутренние значения символа нам тоже не удастся.

Еще одним методом создания символов является метод Symbol.for.


let symbol = Symbol.for('name');

console.log(symbol);



В консоли получим тоже сообщение. что и при первом методе создания символа.



Отличие между двумя способами заключается в том, что если создать еще один сивол методом for и указать тоже самое имя, то мы получим равные символы.


let symbol = Symbol.for('name');
let symbol2 = Symbol.for('name');

console.log(symbol === symbol2);



В консоли получим значение: true.

Первый и второй символы - это один и тот же символ.

Когда мы используем метод for, то он вначале попробует найти символ по имени и если ему это не удастся, то он создаст новый символ.

Благодаря методу for, символ можно получить из любого места в программе, так как они заносятся в так называемый глобальный реестр.

Символы, созданные функцией Symbol не попадают в этот реестр и их нельзя найти с помощью метода for.

Имя символа можно получить методом keyFor.


let symbol = Symbol.for('name');
let name = Symbol.keyFor(symbol);

console.log(name);




В консоли мы увидим - name.

Если в метод .for не передать имя, то получим ошибку

let symbol = Symbol.for();

Будет ошибка!

Использование символов.

В ES5 свойства объектов должны быть строками.

В ES6 они могут быть строками и символами.

Создадим объекта и в качестве второго свойства присвоим символ.


let user = {
 name : 'John',
 [Symbol('password')] : 'r89n123'
};

console.log(user.password);



Вызов функции символа мы не можем использовать в качестве названия свойства, поэтому нам его необходимо было обернуть в квадратные скобки [].

В консоли мы увидим undefined.

Попробуем посмотреть на все свойства объекта -


let user = {
 name : 'John',
 [Symbol('password')] : 'r89n123'
};

console.log(user.password);

console.log(Object.keys(user));



Мы увидим только name.



Попробуем еще и

console.log(Object.getOwnPropertyNames(user));



Как же получить доступ к паролю?

Давайте попробуем использовать метод .for().


let user = {
 name : 'John',
 [Symbol('password')] : 'r89n123'
};

console.log(user.password);

console.log(Object.keys(user));

console.log(Object.getOwnPropertyNames(user));

let password = user[Symbol.for('password')];

console.log(password);



И в консоли опять - undefined.



Если мы создаем символ с помощью функции Symbol(), то он не попадает в глобальный реестр и мы не сможем его найти с помощью метода .for()


выше приведено доказательство этому.

Но если у объекта указать с .for()


let user = {
 name : 'John',
 [Symbol.for('password')] : 'r89n123'
};

console.log(user.password);

console.log(Object.keys(user));

console.log(Object.getOwnPropertyNames(user));

let password = user[Symbol.for('password')];

console.log(password);





В ES6 был добавлен метод, который позволяет посмотреть символы у объектов.

Этот метод находится у объектов Object и называется getOwnPropertySymbols


let user = {
 name : 'John',
 [Symbol.for('password')] : 'r89n123'
};

console.log(user.password);

console.log(Object.keys(user));

console.log(Object.getOwnPropertyNames(user));

let password = user[Symbol.for('password')];

console.log(password);

console.log(Object.getOwnPropertySymbols(user));





Даже если мы уберем .for у объекта, то все равно мы увидим тоже самое сообщение.



А если убрать и имя, то


let user = {
 name : 'John',
 [Symbol()] : 'r89n123'
};

console.log(user.password);

console.log(Object.keys(user));

console.log(Object.getOwnPropertyNames(user));

let password = user[Symbol.for('password')];

console.log(password);

console.log(Object.getOwnPropertySymbols(user));



То мы все равно узнаем, что у объекта есть символ, но какое именно мы не знаем и никак получить к нему доступ мы не можем!



Для того, чтобы это исправить мы создадим отдельную переменную password.


let password = Symbol();

let user = {
 name : 'John',
 [password] : 'r89n123'
};

console.log(user.password);

console.log(Object.keys(user));

console.log(Object.getOwnPropertyNames(user));

// let password = user[Symbol.for('password')];

// console.log(password);

console.log(Object.getOwnPropertySymbols(user));





Результат будет тот же, но теперь символ хранится в переменной.

Смволы были добавлены в язык не для того, чтобы что-то спрятать. Они были добавлены для того, чтобы избежать конфликта имен свойств.

Так мы можем добавить нашему объекту еще одно свойство password.


let password = Symbol();

let user = {
 name : 'John',
 [password] : 'r89n123',
 password: 'a9b9c9n123'
};

console.log(user.password);

console.log(Object.keys(user));

console.log(Object.getOwnPropertyNames(user));

// let password = user[Symbol.for('password')];

// console.log(password);

console.log(Object.getOwnPropertySymbols(user));



В консоли:



При этом прямого доступа к символу нет, но это не значит, что его нет вообще.

При желании мы можем его получить.

Как я и говорил, нам как пользователям языка, нет необходимости создавать свои собственные символы, но в ES6 есть множество встроенных символов.

Эти символы называют - Well-known symbols или хорошо известные символы.

Одним из них является Symbol.iterator. Благодаря этому символу мы можем получить доступ к внутреннему методу объекта.

Выглядит это так:

У объекта должно быть свойство Symbol.iterator. Это свойство является методом, поэтому - круглые скобки и фигурные скобки .

Теперь мы можем добавить объекту свойство iterator и никакого конфликта не будет.


let object = {
 iterator: 0,
 [Symbol.iterator](){}
}



Этот метод, который мы определили, используется конструкцией forof благодаря которой мы можем перебирать или итерировать (отсюда и название символа) объект, у которого определен этот специальный метод.

Об этом мы поговорим уже в следующем посте.





                                                                                                                                                             

четверг, 27 сентября 2018 г.

ES6: Создание обещаний (promises)(XIV-2).

Создание обещаний (promises).

В качестве примера, мы продолжим рассмотрение процесса получения визы.



Все материалы по ES6


ES6 для начинающих (2)

В редакторе откроем новый файл- promises.js. Подключим его в нашем файле

index.html

<script src="src/promises.js"><script>

В этом файле создадим функцию.


function applyForVisa( document ) {
    console.log('Обработка заявления... ');
};

applyForVisa({});



Она называется applyForVisa(). Это функция принимает документы на визу.

В консоли браузера мы выведем - Обработка заявления... . После чего мы в эту функцию вызываем. и в качестве объекта документа отправляем пустой объект - applyForVisa({});

Процесс получения визы займет всего 2 секунды. Эту задержку стимулируем с помощью метода setTimeout ();.

Он принимает функцию и количество миллисекунд.

А что именно произойдет через эти две секунды, мы напишем функции.

Создадим переменную виза. В качестве значения присвоим - пустой объект let visa = {};.

А вот что делать дальше?


function applyForVisa( document ) {
    console.log('Обработка заявления... ');
    setTimeout( function () {
        let visa = {};
       // ??
    }, 2000);
};

applyForVisa({});



Допустим визы мы получили. Как нам передать и обратно?

Если мы напишем return visa; то мы вернем его из функции которые отправляем в

setTimeout, но при этом пользователь визу не получил!

Таким образом вернуть визу мы не можем!

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


applyForVisa({},function( visa ) {
	console.info('Виза получена!');
});



Данная функция получит visa, в качестве параметра. И в консоль браузера мы выедем сообщение - "Виза получена!".

Для вывода в консоль мы будем использовать метод - console.info();.

Теперь функция applyForVisa принимает не только документы ,но и функцию.

Назовем её resolve - то есть разрешить. После того как, мы создали объект виза, вызовем функцию resolve и в качестве аргумента отправим объект visa.


function applyForVisa( document, resolve ) {
    console.log('Обработка заявления... ');
    setTimeout( function () {
        let visa = {};
        resolve( visa );
    }, 2000);
};

applyForVisa( {}, function ( visa ) {
    console.info('Виза получена!');
});



Давайте посмотрим что у нас получилось в консоли.



Мы увидим сообщение- Обработка заявления и через 2 секунды, мы видим сообщение Виза получена.

На данный момент мы отправляем функцию обратного вызова на случай если визу нам одобряют. Если нам её не одобряют, то давайте отправим еще одну функцию обратного вызова.

Данная функция в качестве параметра примет причину - reson, почему нам отказали в визе. Давайте выведем эту причину в консоле браузера Используя метод console/error( reson )

Теперь функция applyForVisa принимает документы, функцию на случай если Виза одобряется, и функцию на случай если в визе отказывают. Назовем эту функцию reject - отказать.

Теперь, давайте немного изменим логику и в 50% случаев будем визу одобрять, a в 50% отказывать.

Для этого воспользуемся Math.random();, чтобы получить случайное число. Если это число будет больше 0,5 то мы вызовем функцию resolve в качестве аргумента отправим пустой объект.

Представим, что это будет виза. В противном случае мы вызовем функцию reject и в качестве аргумента напишем - "В визе отказано: не хватило документов...".

Вызов функции resolve( visa ); мы уберем. Кстати, заметьте, функцию resolve я могу вызывать столько раз сколько я хочу.

Функцию reject я могу звать столько раз сколько я захочу и это может быть очень серьезной проблемой, если я использую какую-то стороннюю библиотеку, потому что этим кодом Я не руковожу!

Решить эту проблему нам помогут обещания! Но об этом чуть позже.


function applyForVisa( document, resolve, reject ) {
    console.log('Обработка заявления... ');
    setTimeout( function () {
        Math.random() > .5 ? resolve ({}) : reject ('В визе отказано: не хватило документов...')
       
    }, 2000);
};

applyForVisa( {}, function ( visa ) {
    console.info('Виза получена!');
},
function ( reson ) {
    console.error( reson );
});



Давайте посмотрим что у нас получилось в консоли.

При перезагрузке страницы мы будем получать два вида сообщений:



Добавим еще две функции.

Первая будет называться bookHotel() - с помощью которой мы сможем зарезервировать номер в отеле, а вторая будет называться buyTickets() - с помощью которой мы сможем купить билеты на самолет.

Логика у них будет очень похоже на функцию applyForVisa. Поэтому просто оставим их пустыми.

После того, как мы получили визу, давайте вызовем функцию bookHotel и допустим что отелю или компании которая занимается бронированием отелей, необходимо отправить визу.

Благо, что мы её уже получили и поэтому можем отправить.

Далее, нам необходимо отправить ещё одну функция обратного вызова на случай если отель нам зарезервировали.

в качестве аргумента отправим reservation. И давайте отправим функцию на случае ошибки.

Ой какой читабельный код получается!!!

На случай Если всё хорошо, и мы зарезервировали номер в отеле, давайте купим билеты.

Для покупки билета нам тоже нужно отправить номер резервации.

И наподобие кода выше: функцию на случае успеха и функцию на случай неудачи.

Если всё правильно записать, то получится достаточно много кода.

Это и есть как раз то, что называют Callback Hell.


function applyForVisa( document, resolve, reject ) {
    console.log('Обработка заявления... ');
    setTimeout( function () {
        Math.random() > .5 ? resolve ({}) : reject ('В визе отказано: не хватило документов...')
       
    }, 2000);
};
function bookHotel () {};

function buyTickets () {};

applyVisa({}, function( visa ){
	console.info('Виза получена!');
	bookHotel( visa, function (reservation){
		buyTikkets(reservation, function () {}, function () {});
	}, function (error) {})
},
function( reson ) {
	console.error( reson );

});



Красным отмечен полный кошмар вложений из функций обратного вызова. Их может быть очень очень много и это сделает код полностью нечитабельным!

Callback Hell - когда у одной функции обратного вызова есть еще функция обратного вызова и у той тоже функция обратного вызова, и так далее.

Таким образом есть две проблемы.
  1. - функции resolve reject могут быть вызваны один, два или три раза, а могут быть не вызваны ниразу. Мы предполагаем, что они будут вызваны один раз, но гарантировать это мы не можем.
  2. - это нечетабельный Callback Hell .
Всё это мы ррешим с помощью обещаний.


function applyForVisa( documents ) {
    console.log('Обработка заявления... ');
    let promise = new Promise( function ( resolve, reject ) {
        setTimeout( function () {
            Math.random() > .5 ? resolve ({}) : reject ('В визе отказано: не хватило документов...')
           
        }, 2000);
        
    });
    return promise;
};

function bookHotel () {};

function buyTickets () {};

applyForVisa({})
    .then(
            function ( visa ) {
            console.info('Виза получена!');
            bookHotel();
            },
            function ( reson ) {
            console.error( reson );
         });



Код должен работать также.

В консоли мы увидим такие же сообщения при перезагрузке страницы.

Перепишем с использованием стрелочных функций:


function applyForVisa( documents ) {
    console.log('Обработка заявления... ');
    let promise = new Promise( function ( resolve, reject ) {
        setTimeout( function () {
            Math.random() > .5 ? resolve ({}) : reject ('В визе отказано: не хватило документов...')
           
        }, 2000);
        
    });
    return promise;
};

function bookHotel () {};

function buyTickets () {};

applyForVisa({})
    .then( visa => console.info('Виза получена!'),
          reson => console.error( reson ));



Для того, чтобы запланировать последующие действия, мы также можем использовать метод .then().

На случай, если обещание будет выполнено нам надо отправить туда одну функцию.

Таким способом мы отправим туда функцию бронирования отеля - boorHotel() и вместо того, чтобы на каждом этапе определять функцию на случай ошибки, мы можем определить одну для всей операции в целом.

Если на каком-то этапе произойдет ошибка, то сработает та функция которую мы сейчас определим.

Для этого, после .then(bookHotel мы сделаем еще один метод . Туда отправим buyTickets, а далее - используя метод .catch у обещания, мы выведем ошибку, которая нам будет передана. При этом, обработчик ошибки у первого метода мы можем убрать!

И для проверки в функцию bookHotel добавим вывод сообщения в консоль - Бронируем отель.


function applyForVisa( documents ) {
    console.log('Обработка заявления... ');
    let promise = new Promise( function ( resolve, reject ) {
        setTimeout( function () {
            Math.random() > .5 ? resolve ({}) : reject ('В визе отказано: не хватило документов...')
           
        }, 2000);
        
    })
    return promise;
}

function bookHotel () {
    console.log('Бронируем отель.')
};

function buyTickets () {};

applyForVisa({})
    .then( visa => console.info('Виза получена!'))
    .then( bookHotel )
    .then( buyTickets )
    .catch( error => console.error( error ));



В результате в консоли мы увидим, что при одобрении визы у нас появится пункт - Бронируем отель.



А если виза не одобрена, то вывод как и в предыдущих случаях .

Можно добавить Покупаем билеты в buyTickets



При отказе в визе мы сразу будем попадать в функцию, которую отправили в метод .catch.

Сейчас мы видим 3 сообщения - виза получена, бронируем отель и покупаем билеты.

Заметьте метод .then мы используем три раза.



Мы используем одно обещание или несколько?

Как минимум одно обещание в методе applyForVisa мы создаем. Далее, мы его возвращаем.

Используем метод .then выводим сообщение в консоль браузера.

Далее, мы используем .then( bookHotel ).

Это одно и то же обещание или нет?

Обещание выполняется или не выполняется только один раз!

То есть обещание может находиться только в одном из состояний.
  1. Оно может быть Panding - Ожидание.
  2. Оно может быть resolved - Выполнена
  3. или rejected - Отказано или не выполнено.
То есть, если я выполнил обещание используя метод resolved, состояние этого обещания я уже не могу поменять на rejected, используя метод reject. Все последующие попытки изменить состояние обещания будут игнорироваться.

Это легко проверить если после Math.random() обещание ещё раз выполним, а потом отменим а потом выполним.


function applyForVisa( documents ) {
    console.log('Обработка заявления... ');
    let promise = new Promise( function ( resolve, reject ) {
        setTimeout( function () {
            Math.random() > .5 ? resolve ({}) : reject ('В визе отказано: не хватило документов...')
           resolve();
           reject();
           resolve();
        }, 2000);
        
    })
    return promise;
}

function bookHotel () {
    console.log('Бронируем отель.');
};

function buyTickets () {
    console.log('Покупаем билеты.');
};

applyForVisa({})
    .then( visa => console.info('Виза получена!'))
    .then( bookHotel )
    .then( buyTickets )
    .catch( error => console.error( error ));
    


Давайте посмотрим что у нас будет в браузере.

Заметьте, мы получили не 6 сообщений или 9, все те же 3.

То есть обещание было выполнено один раз.

Тоже самое с ошибкой. Мы получили только одно сообщение об ошибке.

Для того чтобы иметь возможность использовать методы .then по цепочке, нам необходимо каждый раз создавать и возвращать новое обещание!

Благо, это делает автоматически!

То есть, вот здесь (на фото ниже показано стрелкой), после того как выполняется console.info('Виза получена!')), создается новое обещание, которое передается дальше!



Дальше, новому обещания мы присылаем функцию bookHotel, на случай если оно будет выполнено.

Если мы попадаем функцию bookHotel ,после того как мы выводим сообщение 'Бронируем отель.' создается новое обещание и передается дальше.

Дальше мы увидим сообщение - Покупаем билеты - создаются новые обещания и отправляется дальше.

Новое обещание мы можем использовать а можем не использовать.

Более того, после метода .catch();, мы можем использовать еще раз .then().


function applyForVisa( documents ) {
    console.log('Обработка заявления... ');
    let promise = new Promise( function ( resolve, reject ) {
        setTimeout( function () {
            Math.random() > .5 ? resolve ({}) : reject ('В визе отказано: не хватило документов...')
           resolve();
           reject();
           resolve();
        }, 2000);
        
    })
    return promise;
}

function bookHotel () {
    console.log('Бронируем отель.');
};

function buyTickets () {
    console.log('Покупаем билеты.');
};

applyForVisa({})
    .then( visa => console.info('Виза получена!'))
    .then( bookHotel )
    .then( buyTickets )
    .catch( error => console.error( error ))
    .then(() => console.log('Я всё еще тут.'));



И это сообщение мы увидим в любом случае!

Для того чтобы забронировать отель, нам необходимо предоставить визу, поэтому функция bookHotel () будет принимать в качестве параметра visa.

и для того чтобы нам не отказывали в визе, можно поставить 0 в условном выражении Math.random() > 0 ? .

Давайте посмотрим и мы видим что визу у нас undefined.



Давайте разберемся почему?

Как я уже сказал, когда мы используем .then(), каждый раз создается новое обещание.

Так вот, обещание в котором мы отправляем функцию bookHotel () ничего про объект visa не знает. Для этого нам необходимо объект visa из первого обещания вернуть.

Для этого нам необходимо поставить фигурные скобки после сообщения виза получена и вернуть визу.



function applyForVisa( documents ) {
    console.log('Обработка заявления... ');
    let promise = new Promise( function ( resolve, reject ) {
        setTimeout( function () {
            Math.random() > .5 ? resolve ({}) : reject ('В визе отказано: не хватило документов...')
           resolve();
           reject();
           resolve();
        }, 2000);
        
    })
    return promise;
}

function bookHotel ( visa ) {
    console.log('Бронируем отель.');
    console.log('visa =' + visa);
};

function buyTickets () {
    console.log('Покупаем билеты.');
};

applyForVisa({})
    .then( visa => {
        console.info('Виза получена!');
        return visa; 
    })
    .then( bookHotel )
    .then( buyTickets )
    .catch( error => console.error( error ));
    



Давайте правильно что получилось .



И теперь мы видим visa =[object Object]. То есть нам удалось передать visa из одного обещания в другое.

Создадим функцию получения визы и в нее поместим код:


function applyForVisa( documents ) {
    console.log('Обработка заявления... ');
    let promise = new Promise( function ( resolve, reject ) {
        setTimeout( function () {
            Math.random() > .5 ? resolve ({}) : reject ('В визе отказано: не хватило документов...')
           resolve();
           reject();
           resolve();
        }, 2000);
        
    })
    return promise;
}
function getVisa ( visa ) {
    console.info('Виза получена!');
    return visa; 
}

function bookHotel ( visa ) {
    console.log('Бронируем отель.');
    console.log('visa =' + visa);
};

function buyTickets () {
    console.log('Покупаем билеты.');
};

applyForVisa({})
    .then( getVisa ) 
    .then( bookHotel )
    .then( buyTickets )
    .catch( error => console.error( error ));
    


При проверке получим тот же самый результат.

Если для покупки билета, нам необходимо передать информацию о забронированном отеле, то для этого из функции bookHotel нам нужно вернуть что-то. Давайте вернем пустой объект.

Далее в функции buyTickets мы принимаем параметр booking - так назвали его. И посмотрим на него в консоли.

function buyTickets ( booking ) {
    console.log('Покупаем билеты.');
    console.log('Бронь', booking);
};


И в консоли мы увидим объект Бронь.



Давайте полагаться на JavaScript не будем и с функции getVisa() сами вернём обещание.

В теле просто вызовем resolve( visa ) с параметром - visa.


function getVisa ( visa ) {
    console.info('Виза получена!');
    return new Peomise( function ( resolve, reject ) {
        resolve();
    }); 
}

В консоли мы получим тот же самый результат. Тоже самое сделаем в функции bookHotel Для разнообразия в нее передадим функцию reject('Нет мест!');

function applyForVisa( documents ) {
    console.log('Обработка заявления... ');
    let promise = new Promise( function ( resolve, reject ) {
        setTimeout( function () {
            Math.random() > .5 ? resolve ({}) : reject ('В визе отказано: не хватило документов...')
           resolve();
           reject();
           resolve();
        }, 2000);
        
    })
    return promise;
}
function getVisa ( visa ) {
    console.info('Виза получена!');
    return new Promise( function ( resolve, reject ) {
        resolve();
    }); 
}

function bookHotel ( visa ) {
    console.log('Бронируем отель.');
    console.log('visa =' + visa);
    return new Promise ( function (resolve, reject ) {
        reject('Нет мест!');
    });
};

function buyTickets ( booking ) {
    console.log('Покупаем билеты.');
    console.log('Бронь', booking);
};

applyForVisa({})
    .then( getVisa ) 
    .then( bookHotel )
    .then( buyTickets )
    .catch( error => console.error( error ));
    



В консоли увидим.



До функции -купить билеты мы не дошли.

Если поменять reject('Нет мест!') на resolve({}), то мы пройдем через все функции.

То есть и получим визу и отель и билеты...

В функциях getVisa bookHotel мы сразу выполняем обещания. И если это так, то нет необходимости использовать конструктор.

Например так:


function bookHotel ( visa ) {
    console.log('Бронируем отель.');
    console.log('visa =' + visa);
    return Promise.resolve( visa );
};



Если мы хотим сразу отклонить обещание, то можно сделать сразу reject так - return Promise.reject( 'Some Text' );

В данном случае мы моментально отклоняем обещание.

В функции getVisa мы используем конструктор, но в теле функции мы сразу выполняем обещание.

Давайте это исправим и превратим эту функцию в асинхронную.

для этого воспользуемся методом setTimeout и стрелочной функцией, которой вызовем resolve(visa) и подождем 2 сек.

Таким образом все процедура должна занять 4 сек.


function applyForVisa( documents ) {
    console.log('Обработка заявления... ');
    let promise = new Promise( function ( resolve, reject ) {
        setTimeout( function () {
            Math.random() > 0 ? resolve ({}) : reject ('В визе отказано: не хватило документов...')
           resolve();
           reject();
           resolve();
        }, 2000);
        
    })
    return promise;
}
function getVisa ( visa ) {
    console.info('Виза получена!');
    return new Promise( function ( resolve, reject ) {
        setTimeout( () => resolve( visa ), 2000);
    }); 
}

function bookHotel ( visa ) {
    console.log('Бронируем отель.');
    console.log('visa =' + visa);
    return Promise.resolve( visa );
};

function buyTickets ( booking ) {
    console.log('Покупаем билеты.');
    console.log('Бронь', booking);
};

applyForVisa({})
    .then( getVisa ) 
    .then( bookHotel )
    .then( buyTickets )
    .catch( error => console.error( error ));
    


Теперь будут последовательно, через 2 сек появляться сообщения.

Первое - Обработка заявления...
2 сек
Виза получена! и через 2 сек все остальное.

Думаю, что вам стало более понятно, как работают обещания.

Далее мы рассмотрим применение обещаний и как работать с несколькими обещаниями одновременно.





                                                                                                                                                             

среда, 26 сентября 2018 г.

ES6: Обещания - Promise (XIV-1).

Обещания - Promise

Сегодня мы рассмотрим обещания в ES6 или как они называются по-английски Promise.



Все материалы по ES6


ES6 для начинающих (2)

До ES6 для работы с обещаниями нам приходилось использовать различные библиотеки, но теперь обещание поддерживается во всех современных браузерах.

Давайте разберемся Зачем вообще нужны эти обещания?

Очень часто мы хотим сделать чтобы что-то произошло после того, как произойдет что-то другое.

Допустим, мы кликаем на кнопку и появляется сообщение.

Традиционно, для реализации такого рода взаимодействия, используется функция обратного вызова (callbacks). Если вы когда-нибудь писали функцию которую должна сработать при нажатии на кнопку, то вы знаете что такое функция обратного вызова.



Помимо работы с событиями DOM, функция обратного вызова также используется для общения с сервером посредством ajax.

Допустим мы хотим подгрузить данные сервера.Как только мы их получим, нам нужно сначала их обработать. Далее данные необходимо проверить и только потом отобразить.

При этом мы не хотим сидеть и ждать, когда все эти операции закончится.

Приложение должно работать дальше. Если использовать функции обратного вызова для каждой из этих операций то код может превратиться в так называемый callback hell - когда у функции обратного вызова есть своя функция обратного вызова, у которой есть своя функция обратного вызова и вы вbдите как всё это выглядит.



Для маленьких приложений это не такая большая проблема, но для больших всё это может превратится в очень большую головную боль, особенно при отладке.

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



Заметьте насколько он компактен и легко читается.

Давайте наглядно посмотрим как работают обещание.

Допустим вы хотите поехать отдыхать в другую сторону.

Для этого вам необходимо:
  1. - сначала получить визу
  2. - забронировать номер в отеле,
  3. - и в конце купить билет на самолет.


Все эти действия aсинхронны. Это означает, что при подаче документов мы не будем сидеть и ждать результата. Мы пойдём заниматься дальше своими делами.

Сначала вы заполняете документы и отправлять их в посольство.

От посольства вы получаете обещание, что они дадут вам знать получили вы визу или нет.

Пока обещание выполняется оно находится в состоянии Pending ( ожидание ). Далее обещание может быть сдержанным, в таком случае она будет в состоянии Resolved, или несдержанно. В таком случае мы получим ошибку Error и обещание будет находиться в состоянии Rejected.



В зависимости от результата, вы можете спланировать свои дальнейшие действия. Если вы получаете визу тогда вы бронируете номер в отеле .Если всё хорошо, то вы покупаете билет на самолет.

Если какой-то из шагов не выполняется, то вы полностью отменяете отпуск.

Давайте посмотрим как это будет выглядеть в JavaScript.

Допустим имеется функция applyForVisa(); - подать документы на визу.

Эта функция асинхронная и поэтому она вернет обратно - обещание.

let promise = applyForVisa();

Это обещание мы сохраним в переменной promise.

У обещания есть метод .then() что по-русски можно перевести как далее.

Данный метод принимает два аргумента.


let promise = applyForVisa();

promise.then( resolve, reject );



В качестве первого, принимается функция, которая работает в случае если обещание выполняется. В качестве второго аргумента, принимается функция которая сработает если обещание не будет выполнено.

В зависимости от результата выполнения функции applyForVisa(); сработает либо функция resolve или функция reject.

В нашем случае в качестве функции resolve мы отправляем функцию bookHotel или забронировать отель. В качестве функции reject мы отправляем cancelVacation, то есть отменить отпуск.



Как я уже сказал, результатом applyForVisa(); является обещание, но нам не обязательно хранить его в переменной, поэтому сразу после вызова функции applyForVisa(); мы можем использовать метод .then() и в качестве аргументов отправить соответствующие функции.



В функцию .then() не обязательно отправлять два аргумента. Мы можем отправить только один - bookHotel на тот случай если обещание будет выполнено.

Если же обещание не будет выполнено, то мы можем использовать другой метод у объекта promise.

Он называется .catch().


applyForVisa()
 .then( bookHotel )
 .catch( cancelVacation );



Он принимает лишь один аргумент, которым будет являться функция, которая должна сработать если обещали не будет выполнено.

В данном случае cancelVacation.

Метод .catch() имеет смысл использовать если у нас имеется несколько шагов.


applyForVisa()
 .then( bookHotel )
 .then( buyTickeys )
 .catch( cancelVacation );



То есть мы бронируем отель - bookHotel.
Далее Мы покупаем билеты buyTickeys
и если вдруг на каком-то из шагов происходит ошибка, то мы и ловим (.catch) функцией cancelVacation.

Заметьте как просто читается код.
  • Подать заявление на визу.
  • Далее - забронировать отель.
  • Далее - купить билеты.
  • В случае если что-то не получится, то - отменить отпуск.
Асинхронный код выглядит так, как будто он синхронный.

По сути, обещания делают тоже самое, что и функции обратного вызова. Более того, мы и используем функции обратного вызова!

Отличается то, как мы пишем код.Его структура.Её гораздо проще понимать.

Теперь, перейдем к созданию собственных обещаний.



                                                                                                                                                             

понедельник, 24 сентября 2018 г.

ES6: Деструктивное присваивание объектов (XIII).

Деструктивное присваивание объектов.





Все материалы по ES6


Лучше всего это рассмотреть на примере.

Представьте. что у нас есть некий объект и нам нужно получить доступ к его свойствам. В ES5 мы бы сделали просто - присвоили свойства объекта переменным вот таким образом:


var person = {
 firstName : 'John',
 lastName : 'Gray'
};

var firstName = person.firstName;
var lastName = person.lastName;

console.log( firstName, lastName );



В ES6 все это можно уместить в одну строку применив деструктивное присваивание объектов.

Для этого переменные мы объявим в фигурных скобках и в качестве значения присвоим объект.

Важно, чтобы названия переменных совпадали с названием свойств, в этом случае.


let person = {
 firstName : 'John',
 lastName : 'Gray'
};

let { firstName, lastName } = person;

console.log( firstName, lastName );



Если нам требуется использовать переменные с другим именем, то нам можно сделать вот так:


let person = {
 firstName : 'John',
 lastName : 'Gray'
};

let { firstName : first , lastName : last } = person;

console.log( first, last );



Мы просто указали новые названия переменных после двоеточия.

Возможно, было бы более понятным, если бы название переменной было слева, а значение свойств справа, но... Есть так, как есть.

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



let { firstName : first , lastName : last } = { firstName : 'John',lastName : 'Gray'};

console.log( first, last );



В данном случае мы также в консоли получим John Gray.



Значения по умолчанию.

Значения по умолчанию можно указать, если у объекта нет таких свойств, или нет значения у такого свойства.


let { firstName : first , lastName : last, age = 35 } = { firstName : 'John',lastName : 'Gray'};

console.log( first, last, age );





Динамическое вычисление названий свойств.

Имена свойств можно определять динамически с помощью выражений в квадратных скобках.


let { ['first' + 'Name'] : first , lastName : last, age = 35 } = { firstName : 'John',lastName : 'Gray'};

console.log( first, last, age );



В консоли мы увидим тоже самое.

Извлечение значений свойств вложенных объектов.

Мы можем извлечь свойства объектов, являющихся свойствами других объектов.

На примере. Создадим новый объект у которого свойство social будет являться объектом.


let user = { 
 firstName : 'John',
 lastName : 'Gray',
 social : {
  facebook: 'GrayJohn-Facebook',
  twitter: 'J-G-Twitter'
 }
}



Таким образом, чтобы получить доступ к свойству фейсбук, нам необходимо -


let user = { 
 firstName : 'John',
 lastName : 'Gray',
 social : {
  facebook: 'GrayJohn-Facebook',
  twitter: 'J-G-Twitter'
 }
}

let { firstName : first , lastName : last, social:{facebook},  age = 35 } = user;

console.log( first, last, facebook, age );





Таким образом с вложенными объектами, мы вначале указываем название основного свойства - social, а потом после двоеточия и в фигурных скобках - название вложенного свойства.

Если бы свойство facebook содержало бы в себе еще один объект, то мы сделали бы вот так:


let { firstName : first , lastName : last, social:{facebook : { имя_свойства } },  age = 35 } = user;



Если мы хотим изменить имя переменной facebook, то мы можем указать новое имя свойства через двоеточие:


let { firstName : first , lastName : last, social:{ facebook : fb },  age = 35 } = user;

console.log( first, last, fb, age );



Результат в консоли не изменится.

Деструктивное присваивание объекта в качестве параметра функции.

Лучше всего рассмотреть на примере.

Создадим функцию, которая будет принимать два параметра, один из которых - config, будет объектом.

У этого объекта будет два свойства - data и cach.

Если бы мы писали код на ES5, то поступили бы так:


function post ( url, config ) {
 config.data;
 config.cach;
};



В ES6 мы сделаем это иначе.

Вначале создадим переменную в которую поместим результат вызова функции post с параметрами...

let user = { 
 firstName : 'John',
 lastName : 'Gray',
 social : {
  facebook: 'GrayJohn-Facebook',
  twitter: 'J-G-Twitter'
 }
}

let result = post ( 'api/users', { data : user, cach : false });



И теперь используя деструктивное присваивание, мы можем вместо переменной (параметра функции) - congig, сразу передать свойства data и cach в фигурных скобках:


let user = { 
 firstName : 'John',
 lastName : 'Gray',
 social : {
  facebook: 'GrayJohn-Facebook',
  twitter: 'J-G-Twitter'
 }
}

let { firstName : first , lastName : last, social:{ facebook : fb },  age = 35 } = user;

console.log( first, last, fb, age );



function post ( url, { data, cach } ) {
 console.log( data, cach );
};

let result = post ( 'api/users', { data : user, cach : false });







Если, вместо data, мы хотим сразу получить доступ к свойства firstName и lastName, то мы можем сделать таким образом:


function post ( url, { data : { firstName, lastName }, cach } ) {
 console.log( firstName, lastName, cach );
};

let result = post ( 'api/users', { data : user, cach : false });





обратите внимание на то, что в параметре присутствует свойство data, но мы его используем только для того, чтобы добраться до свойств firstName и lastName.

Если попробовать вывести в консоли свойство data, то получим ошибку - Uncaught ReferenceError: data is not defined.

Деструктивное присваивание для возврата значений.

Для примера создадим функцию, которая будет возвращать литерал объекта.


 function getUserInfo () {
  return  {
   firstName : 'John',
         lastName : 'Gray',
         social : {
  facebook: 'GrayJohn-Facebook',
  twitter: 'J-G-Twitter'
  }
 };
};

let { firstName, lastName, social:{ twitter}} = getUserInfo();
console.log(firstName, lastName, twitter);





Здесь все очень похоже на то, что было выше. Ничего сложного, если понимать, то как происходит деструктивное присваивание.


Хотите освоить самые современные методы написания React приложений? Надоели простые проекты? Нужны курсы, книги, руководства, индивидуальные занятия по React и не только? Хотите стать разработчиком полного цикла, освоить стек MERN, или вы только начинаете свой путь в программировании, и не знаете с чего начать, то пишите через форму связи, подписывайтесь на мой канал в Телеге, вступайте в группу на Facebook.


Удачного кодирования!                                                                                                                                                              

Телеграм канал - Full Stack JavaScript Developer
Помочь проекту (любая валюта). DONATE

воскресенье, 23 сентября 2018 г.

ES6: Деструктивное присваивание массивов (XII).

Деструктивное присваивание

Синтаксис деструктивного присваивания в выражениях JavaScript позволяет извлекать данные из массивов или объектов при помощи синтаксиса, подобного объявлению массива или литералов в объекте.

Говоря простыми словами - это синтаксис, который облегчает извлечение данных из массивов и объектов.





Все материалы по ES6


Первое, что мы сделаем, это запустим скрипт Babel, который будет следить за файлами и при их изменении создавать свежие версии.

Откроем папку проекта в командной строке (КС). Вводим команду:

npm run watch


И нажать Enter

В папке src создадим файл destructor.js и сразу укажу его в файле index.html

<script src="src/destructor.js"></script>

Последние версии браузеров поддерживают синтаксис деструктивного присваивания без ранспиляции.

Деструктивное присваивание массивов

Создадим массив. Для извлечения данных из этого массива в ES5:


var cats = ['cat1', 'cat2', 'cat3','cat4'];

var cat_1 = cats[0];
var cat_2 = cats[1];
var cat_3 = cats[2];
var cat_4 = cats[3];

console.log(cat_1, cat_2, cat_3, cat_4);



В ES6 это можно сделать проще.


let cat_1, cat_2, cat_3, cat_4; //объявили переменные
[ cat_1, cat_2, cat_3, cat_4 ] = cats; //присвоили им значения

console.log(cat_1, cat_2, cat_3, cat_4);



Мы получим тот же результат - вывод в консоли всех значений массива.

Еще немного упростим:


let [ cat_1, cat_2, cat_3, cat_4 ] = cats;

console.log(cat_1, cat_2, cat_3, cat_4);



Массив можно объявить сразу в выражении:


let [ cat_1, cat_2, cat_3, cat_4 ] = ['cat1', 'cat2', 'cat3','cat4'];

console.log(cat_1, cat_2, cat_3, cat_4);



Если посмотреть на то, что сделал Babel, то можно увидеть, что он просто создал переменные и присвоил им значения.

Файл dist/destructor.js


'use strict';

// var cat_1 = cats[0];
// var cat_2 = cats[1];
// var cat_3 = cats[2];
// var cat_4 = cats[3];

var cat_1 = 'cat1',
    cat_2 = 'cat2',
    cat_3 = 'cat3',
    cat_4 = 'cat4';


console.log(cat_1, cat_2, cat_3, cat_4);



Квадратные скобки при объявлении переменных говорит о том. что мы будем использовать деструктивное присваивание. В качестве значения мы присваиваем им массив или переменную, содержащую массив.

Далее, благодаря деструктивному присваиванию значения массива копируются в созданные нами переменные.

Таким образом мы получаем переменные со значениями массива.

Если переменных больше, чем значений массива.

Если переменных больше, чем значений массива, то значение переменной будет не определено -

undefined.


let items = [11, 55, 99];

let [ low, mid, height, best ] = items;

console.log( low, mid, height, best );



В консоли мы увидим:



Есть возможность игнорировать значения. Для этого мы не указываем переменную, но оставляем запятую.

Например так:


let items = [11, 55, 99];

let [ low,, best ] = items;

console.log( low, best );



В консоли получим 11 и 99.

В массиве больше значений, чем переменных.

Для последней переменной можно использовать синтаксис оставшихся параметров - три точки ....

В этом случае переменная будет принимать массив.


let items = [11, 55, 99];

let [ low,...rest] = items;

console.log( rest );



В консоли мы увидим, что в переменной rest содержится массив.



Мы можем использовать значение по умолчанию.

В массиве меньше значений, чем переменных.




let items = [11, 55];

let [ low, mid, height = 0 ] = items;

console.log( low, mid, height );



В консоли 11, 55 и 0.

Вложенный массаив.

Деструктивное присваивание можно использовать и в тех случаях, когда массив содержит в себе другой массив.


let items = [11, 55, [ 2, 4] ];

let [ low, mid, height ] = items;

console.log( height );



Значением переменной height будет массив:



Для того, чтобы вытащить значения из вложенного массива, мы еще раз используем квадратные скобки.


let items = [11, 55, [ 2, 4] ];

let [ low, mid, [ two, four ] ] = items;

console.log( low, mid, two, four );





Как параметр функции.

Деструктивное присваивание мы можем использовать как параметр функции.

Создадим простую функцию и вызовем ее. В качестве аргументов передадим массив. Для того, чтобы вытащить значения из массива, мы можем использовать деструктивное присваивание.


function compute ( [ tree, five ] ) {
 console.log("tree = " + tree);
 console.log("five = " + five)
}

compute([3, 5]);



Как и ожидалось, в консоли мы получим :



Возврат значений функцией.

Деструктивное присваивание мы можем использовать для возврата значений из функций.

Допустим у нас есть функция, которая возвращает массив из трех значений.

Далее объявим переменную в которую пометим вызов этой функции.


function getNumbers () {
 return [ 33, 44, 77];
}

let numbers = getNumbers ();

console.log( numbers );



Как можно предположить, мы получим в консоли массив из трех элементов:



Теперь, используя деструктивное присваивание мы можем объявить переменные и присвоить им значения возвращенного массива.

Для этого в качестве значения мы присвоим результат вызова нашей функции.


function getNumbers () {
 return [ 33, 44, 77];
}

let numbers = getNumbers ();

console.log( numbers );

let [t_tree, f_four, s_seven] = getNumbers();

console.log( t_tree, f_four, s_seven );





Быстрый обмен значениям переменных.

Деструктивное присваивание используется для быстрого обмена значений переменных.

Проще всего это увидеть на примере:


let yes = 'yes';
let no = 'no';

[ yes, no ] = [ no, yes];

console.log("yes = " + yes);
console.log("no = " + no);



В консоли мы увидим:



Если поcмотреть на то, что сделал Babel dist/destructor.js


'use strict';

var _slicedToArray = function () { function sliceIterator(arr, i) { var _arr = []; var _n = true; var _d = 

false; var _e = undefined; try { for (var _i = arr[Symbol.iterator](), _s; !(_n = (_s = _i.next()).done); _n 

= true) { _arr.push(_s.value); if (i && _arr.length === i) break; } } catch (err) { _d = true; _e = err; } 

finally { try { if (!_n && _i["return"]) _i["return"](); } finally { if (_d) throw _e; } } return _arr; } 

return function (arr, i) { if (Array.isArray(arr)) { return arr; } else if (Symbol.iterator in Object(arr)) 

{ return sliceIterator(arr, i); } else { throw new TypeError("Invalid attempt to destructure non-iterable 

instance"); } }; }();

// let items = [11, 55, [ 2, 4] ];

// let [ low, mid, [ two, four ] ] = items;

// console.log( low, mid, two, four );

// function compute ( [ tree, five ]) {
//  console.log("tree = " + tree);
//  console.log("five = " + five)
// }

// compute([3, 5]);

function getNumbers() {
 return [33, 44, 77];
}

var numbers = getNumbers();

console.log(numbers);

var _getNumbers = getNumbers(),
    _getNumbers2 = _slicedToArray(_getNumbers, 3),
    t_tree = _getNumbers2[0],
    f_four = _getNumbers2[1],
    s_seven = _getNumbers2[2];

console.log(t_tree, f_four, s_seven);

var yes = 'yes';
var no = 'no';

var _ref = [no, yes];
yes = _ref[0];
no = _ref[1];


console.log("yes = " + yes);
console.log("no = " + no);



По сути, если разобраться то, он делает все тоже самое. что мы бы написали сами в ES5.








                                                                                                                                                             


Хотите освоить самые современные методы написания React приложений? Надоели простые проекты? Нужны курсы, книги, руководства, индивидуальные занятия по React и не только? Хотите стать разработчиком полного цикла, освоить стек MERN, или вы только начинаете свой путь в программировании, и не знаете с чего начать, то пишите через форму связи, подписывайтесь на мой канал в Телеге, вступайте в группу на Facebook.Пишите мне - kolesnikovy70 почта gmail.com