Translate

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

четверг, 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 сек все остальное.

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

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





                                                                                                                                                             

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

Отправить комментарий



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