.gdpr{position: fixed; top: 0; bottom: 0; left: 0; right: 0; background: rgba(0, 0, 0, 0.7);color: #333;z-index:9999999;line-height:1.3;height: 100vh;width: 100vw} .gdpr_w{padding: 2rem;background: #fff;max-width: 700px;width: 95%;margin: 5% auto;text-align: center;position:fixed;left: 0;right: 0;margin:10% auto;} .gdpr_t{margin-bottom:15px;} .gdpr_t h3{font-size: 30px;margin:0px 0 10px 0;} .gdpr_t p{font-size: 16px;line-height: 1.45;margin:0;} .gdpr_x {position: absolute; right: 24px; top: 16px; cursor:pointer;} .gdpr_yn{margin-top:10px;} .gdpr_yn form{display: inline;} .gdpr_yn button{background: #37474F;border: none;color: #fff;padding: 8px 30px;font-size: 13px;margin: 0 3px;} .gdpr_yn .gdpr_n{background: #fff;color: #222;border: 1px solid #999;} amp-consent{margin-left: 10px;top: 2px;width: auto;background: transparent;} .gdpr_fmi{ width:100%; font-size: 15px; line-height: 1.45; margin: 0; } #footer .gdpr_fmi span, .gdpr_fmi span { display: inline-block; } #footer .gdpr_fmi a{ color: #005be2; } @media(max-width:768px){ .gdpr_w{width: 85%;margin:0 auto;padding:1.5rem;} } @media(max-width:700px){ .gdpr_w{margin:0 auto; width: 85%;} } .gdpr_fmi a:before{ display:none; } .gdpr_w{width:100%;} .f-w-f2 { padding: 50px 0px; } footer amp-consent.amp-active { z-index:9999; display: initial; position: inherit; height:20px; width:100%; } body[class*="amp-iso-country-"] .amp-active{ display: contents; } #post-consent-ui { position: fixed; z-index: 9999; left: 45%; margin-top: 10px; top: 0; } @font-face { font-family: Poppins;font-display: optional;font-style: normal;font-weight: 400;src: local('Poppins Regular '), local('Poppins-Regular'), url(https://fonts.gstatic.com/s/poppins/v13/pxiEyp8kv8JHgFVrFJDUc1NECPY.ttf);}@font-face { font-family: Poppins;font-display: optional;font-style: normal;font-weight: 500;src: local('Poppins Medium '), local('Poppins-Medium'), url(https://fonts.gstatic.com/s/poppins/v13/pxiByp8kv8JHgFVrLGT9V1tvFP-KUEg.ttf);}@font-face { font-family: Poppins;font-display: optional;font-style: normal;font-weight: 700;src: local('Poppins Bold '), local('Poppins-Bold'), url(https://fonts.gstatic.com/s/poppins/v13/pxiByp8kv8JHgFVrLCz7V1tvFP-KUEg.ttf);}a.heateor_sss_amp{padding:0 4px}div.heateor_sss_horizontal_sharing a amp-img{display:inline-block}.heateor_sss_amp_instagram img{background-color:#624E47}.heateor_sss_amp_yummly img{background-color:#E16120}.heateor_sss_amp_youtube img{background-color:#ff0000}.heateor_sss_amp_buffer img{background-color:#000}.heateor_sss_amp_delicious img{background-color:#53BEEE}.heateor_sss_amp_facebook img{background-color:#3C589A}.heateor_sss_amp_digg img{background-color:#006094}.heateor_sss_amp_email img{background-color:#649A3F}.heateor_sss_amp_float_it img{background-color:#53BEEE}.heateor_sss_amp_linkedin img{background-color:#0077B5}.heateor_sss_amp_pinterest img{background-color:#CC2329}.heateor_sss_amp_print img{background-color:#FD6500}.heateor_sss_amp_reddit img{background-color:#FF5700}.heateor_sss_amp_stocktwits img{background-color:#40576F}.heateor_sss_amp_mewe img{background-color:#007da1}.heateor_sss_amp_mix img{background-color:#ff8226}.heateor_sss_amp_tumblr img{background-color:#29435D}.heateor_sss_amp_twitter img{background-color:#55acee}.heateor_sss_amp_vkontakte img{background-color:#5E84AC}.heateor_sss_amp_yahoo img{background-color:#8F03CC}.heateor_sss_amp_xing img{background-color:#00797D}.heateor_sss_amp_instagram img{background-color:#527FA4}.heateor_sss_amp_whatsapp img{background-color:#55EB4C}.heateor_sss_amp_aim img{background-color:#10ff00}.heateor_sss_amp_amazon_wish_list img{background-color:#ffe000}.heateor_sss_amp_aol_mail img{background-color:#2A2A2A}.heateor_sss_amp_app_net img{background-color:#5D5D5D}.heateor_sss_amp_baidu img{background-color:#2319DC}.heateor_sss_amp_balatarin img{background-color:#fff}.heateor_sss_amp_bibsonomy img{background-color:#000}.heateor_sss_amp_bitty_browser img{background-color:#EFEFEF}.heateor_sss_amp_blinklist img{background-color:#3D3C3B}.heateor_sss_amp_blogger_post img{background-color:#FDA352}.heateor_sss_amp_blogmarks img{background-color:#535353}.heateor_sss_amp_bookmarks_fr img{background-color:#E8EAD4}.heateor_sss_amp_box_net img{background-color:#1A74B0}.heateor_sss_amp_buddymarks img{background-color:#ffd400}.heateor_sss_amp_care2_news img{background-color:#6EB43F}.heateor_sss_amp_citeulike img{background-color:#2781CD}.heateor_sss_amp_comment img{background-color:#444}.heateor_sss_amp_diary_ru img{background-color:#E8D8C6}.heateor_sss_amp_diaspora img{background-color:#2E3436}.heateor_sss_amp_dihitt img{background-color:#FF6300}.heateor_sss_amp_diigo img{background-color:#4A8BCA}.heateor_sss_amp_douban img{background-color:#497700}.heateor_sss_amp_draugiem img{background-color:#ffad66}.heateor_sss_amp_dzone img{background-color:#fff088}.heateor_sss_amp_evernote img{background-color:#8BE056}.heateor_sss_amp_facebook_messenger img{background-color:#0084FF}.heateor_sss_amp_fark img{background-color:#555}.heateor_sss_amp_fintel img{background-color:#087515}.heateor_sss_amp_flipboard img{background-color:#CC0000}.heateor_sss_amp_folkd img{background-color:#0F70B2}.heateor_sss_amp_google_classroom img{background-color:#FFC112}.heateor_sss_amp_google_bookmarks img{background-color:#CB0909}.heateor_sss_amp_google_gmail img{background-color:#E5E5E5}.heateor_sss_amp_hacker_news img{background-color:#F60}.heateor_sss_amp_hatena img{background-color:#00A6DB}.heateor_sss_amp_instapaper img{background-color:#EDEDED}.heateor_sss_amp_jamespot img{background-color:#FF9E2C}.heateor_sss_amp_kakao img{background-color:#FCB700}.heateor_sss_amp_kik img{background-color:#2A2A2A}.heateor_sss_amp_kindle_it img{background-color:#2A2A2A}.heateor_sss_amp_known img{background-color:#fff101}.heateor_sss_amp_line img{background-color:#00C300}.heateor_sss_amp_livejournal img{background-color:#EDEDED}.heateor_sss_amp_mail_ru img{background-color:#356FAC}.heateor_sss_amp_mendeley img{background-color:#A70805}.heateor_sss_amp_meneame img{background-color:#FF7D12}.heateor_sss_amp_mixi img{background-color:#EDEDED}.heateor_sss_amp_myspace img{background-color:#2A2A2A}.heateor_sss_amp_netlog img{background-color:#2A2A2A}.heateor_sss_amp_netvouz img{background-color:#c0ff00}.heateor_sss_amp_newsvine img{background-color:#055D00}.heateor_sss_amp_nujij img{background-color:#D40000}.heateor_sss_amp_odnoklassniki img{background-color:#F2720C}.heateor_sss_amp_oknotizie img{background-color:#fdff88}.heateor_sss_amp_outlook_com img{background-color:#0072C6}.heateor_sss_amp_papaly img{background-color:#3AC0F6}.heateor_sss_amp_pinboard img{background-color:#1341DE}.heateor_sss_amp_plurk img{background-color:#CF682F}.heateor_sss_amp_pocket img{background-color:#f0f0f0}.heateor_sss_amp_polyvore img{background-color:#2A2A2A}.heateor_sss_amp_printfriendly img{background-color:#61D1D5}.heateor_sss_amp_protopage_bookmarks img{background-color:#413FFF}.heateor_sss_amp_pusha img{background-color:#0072B8}.heateor_sss_amp_qzone img{background-color:#2B82D9}.heateor_sss_amp_refind img{background-color:#1492ef}.heateor_sss_amp_rediff_mypage img{background-color:#D20000}.heateor_sss_amp_renren img{background-color:#005EAC}.heateor_sss_amp_segnalo img{background-color:#fdff88}.heateor_sss_amp_sina_weibo img{background-color:#ff0}.heateor_sss_amp_sitejot img{background-color:#ffc800}.heateor_sss_amp_skype img{background-color:#00AFF0}.heateor_sss_amp_sms img{background-color:#6ebe45}.heateor_sss_amp_slashdot img{background-color:#004242}.heateor_sss_amp_stumpedia img{background-color:#EDEDED}.heateor_sss_amp_svejo img{background-color:#fa7aa3}.heateor_sss_amp_symbaloo_feeds img{background-color:#6DA8F7}.heateor_sss_amp_telegram img{background-color:#3DA5f1}.heateor_sss_amp_trello img{background-color:#1189CE}.heateor_sss_amp_tuenti img{background-color:#0075C9}.heateor_sss_amp_twiddla img{background-color:#EDEDED}.heateor_sss_amp_typepad_post img{background-color:#2A2A2A}.heateor_sss_amp_viadeo img{background-color:#2A2A2A}.heateor_sss_amp_viber img{background-color:#8B628F}.heateor_sss_amp_wanelo img{background-color:#fff}.heateor_sss_amp_webnews img{background-color:#CC2512}.heateor_sss_amp_wordpress img{background-color:#464646}.heateor_sss_amp_wykop img{background-color:#367DA9}.heateor_sss_amp_yahoo_mail img{background-color:#400090}.heateor_sss_amp_yahoo_messenger img{background-color:#400090}.heateor_sss_amp_yoolink img{background-color:#A2C538}.heateor_sss_amp_youmob img{background-color:#3B599D}.heateor_sss_amp_gentlereader img{background-color:#46aecf}.heateor_sss_amp_threema img{background-color:#2A2A2A}.heateor_sss_vertical_sharing{position:fixed;left:11px;z-index:99999}.heateor-total-share-count .sss_share_count{color:#666;font-size:23px}.heateor-total-share-count .sss_share_lbl{color:#666}.amp-wp-enforced-sizes img[alt="Pinterest"]{background:#cc2329}.amp-wp-enforced-sizes img[alt="Viber"]{background:#8b628f}.amp-wp-enforced-sizes img[alt="Print"]{background:#fd6500}.amp-wp-enforced-sizes img[alt="Threema"]{background:#2a2a2a}.amp-wp-article-content .heateor_sss_vertical_sharing{left:5px}.amp-wp-article-content amp-img[alt="Pinterest"]{left:4px}.amp-wp-enforced-sizes img[alt="MySpace"]{background:#2a2a2a} amp-web-push-widget button.amp-subscribe { display: inline-flex; align-items: center; border-radius: 5px; border: 0; box-sizing: border-box; margin: 0; padding: 10px 15px; cursor: pointer; outline: none; font-size: 15px; font-weight: 500; background: #4A90E2; margin-top: 7px; color: white; box-shadow: 0 1px 1px 0 rgba(0, 0, 0, 0.5); -webkit-tap-highlight-color: rgba(0, 0, 0, 0); } a.heateor_sss_amp{padding:0 4px;}div.heateor_sss_horizontal_sharing a amp-img{display:inline-block;}.heateor_sss_amp_gab img{background-color:#25CC80}.heateor_sss_amp_parler img{background-color:#892E5E}.heateor_sss_amp_gettr img{background-color:#E50000}.heateor_sss_amp_instagram img{background-color:#624E47}.heateor_sss_amp_yummly img{background-color:#E16120}.heateor_sss_amp_youtube img{background-color:#ff0000}.heateor_sss_amp_teams img{background-color:#5059c9}.heateor_sss_amp_google_translate img{background-color:#528ff5}.heateor_sss_amp_x img{background-color:#2a2a2a}.heateor_sss_amp_rutube img{background-color:#14191f}.heateor_sss_amp_buffer img{background-color:#000}.heateor_sss_amp_delicious img{background-color:#53BEEE}.heateor_sss_amp_rss img{background-color:#e3702d}.heateor_sss_amp_facebook img{background-color:#0765FE}.heateor_sss_amp_perplexity img{background-color:#165962}.heateor_sss_amp_claude img{background-color:#D97757}.heateor_sss_amp_google_ai img{background-color:#000}.heateor_sss_amp_grok img{background-color:#000}.heateor_sss_amp_chatgpt img{background-color:#000}.heateor_sss_amp_digg img{background-color:#006094}.heateor_sss_amp_email img{background-color:#649A3F}.heateor_sss_amp_float_it img{background-color:#53BEEE}.heateor_sss_amp_linkedin img{background-color:#0077B5}.heateor_sss_amp_pinterest img{background-color:#CC2329}.heateor_sss_amp_print img{background-color:#FD6500}.heateor_sss_amp_reddit img{background-color:#FF5700}.heateor_sss_amp_mastodon img{background-color:#6364FF}.heateor_sss_amp_stocktwits img{background-color: #40576F}.heateor_sss_amp_mewe img{background-color:#007da1}.heateor_sss_amp_mix img{background-color:#ff8226}.heateor_sss_amp_tumblr img{background-color:#29435D}.heateor_sss_amp_twitter img{background-color:#55acee}.heateor_sss_amp_vkontakte img{background-color:#0077FF}.heateor_sss_amp_yahoo img{background-color:#8F03CC}.heateor_sss_amp_xing img{background-color:#00797D}.heateor_sss_amp_instagram img{background-color:#527FA4}.heateor_sss_amp_whatsapp img{background-color:#55EB4C}.heateor_sss_amp_aim img{background-color: #10ff00}.heateor_sss_amp_amazon_wish_list img{background-color: #ffe000}.heateor_sss_amp_aol_mail img{background-color: #2A2A2A}.heateor_sss_amp_app_net img{background-color: #5D5D5D}.heateor_sss_amp_balatarin img{background-color: #fff}.heateor_sss_amp_bibsonomy img{background-color: #000}.heateor_sss_amp_bitty_browser img{background-color: #EFEFEF}.heateor_sss_amp_blinklist img{background-color: #3D3C3B}.heateor_sss_amp_blogger_post img{background-color: #FDA352}.heateor_sss_amp_blogmarks img{background-color: #535353}.heateor_sss_amp_bookmarks_fr img{background-color: #E8EAD4}.heateor_sss_amp_box_net img{background-color: #1A74B0}.heateor_sss_amp_buddymarks img{background-color: #ffd400}.heateor_sss_amp_care2_news img{background-color: #6EB43F}.heateor_sss_amp_comment img{background-color: #444}.heateor_sss_amp_diary_ru img{background-color: #E8D8C6}.heateor_sss_amp_diaspora img{background-color: #2E3436}.heateor_sss_amp_dihitt img{background-color: #FF6300}.heateor_sss_amp_diigo img{background-color: #4A8BCA}.heateor_sss_amp_douban img{background-color: #497700}.heateor_sss_amp_draugiem img{background-color: #ffad66}.heateor_sss_amp_evernote img{background-color: #8BE056}.heateor_sss_amp_facebook_messenger img{background-color: #0084FF}.heateor_sss_amp_fark img{background-color: #555}.heateor_sss_amp_fintel img{background-color: #087515}.heateor_sss_amp_flipboard img{background-color: #CC0000}.heateor_sss_amp_folkd img{background-color: #0F70B2}.heateor_sss_amp_google_news img{background-color: #4285F4}.heateor_sss_amp_google_classroom img{background-color: #FFC112}.heateor_sss_amp_google_gmail img{background-color: #E5E5E5}.heateor_sss_amp_hacker_news img{background-color: #F60}.heateor_sss_amp_hatena img{background-color: #00A6DB}.heateor_sss_amp_instapaper img{background-color: #EDEDED}.heateor_sss_amp_jamespot img{background-color: #FF9E2C}.heateor_sss_amp_kakao img{background-color: #FCB700}.heateor_sss_amp_kik img{background-color: #2A2A2A}.heateor_sss_amp_kindle_it img{background-color: #2A2A2A}.heateor_sss_amp_known img{background-color: #fff101}.heateor_sss_amp_line img{background-color: #00C300}.heateor_sss_amp_livejournal img{background-color: #EDEDED}.heateor_sss_amp_mail_ru img{background-color: #356FAC}.heateor_sss_amp_mendeley img{background-color: #A70805}.heateor_sss_amp_meneame img{background-color: #FF7D12}.heateor_sss_amp_mixi img{background-color: #EDEDED}.heateor_sss_amp_myspace img{background-color: #2A2A2A}.heateor_sss_amp_netlog img{background-color: #2A2A2A}.heateor_sss_amp_netvouz img{background-color: #c0ff00}.heateor_sss_amp_newsvine img{background-color: #055D00}.heateor_sss_amp_nujij img{background-color: #D40000}.heateor_sss_amp_odnoklassniki img{background-color: #F2720C}.heateor_sss_amp_oknotizie img{background-color: #fdff88}.heateor_sss_amp_outlook_com img{background-color: #0072C6}.heateor_sss_amp_papaly img{background-color: #3AC0F6}.heateor_sss_amp_pinboard img{background-color: #1341DE}.heateor_sss_amp_plurk img{background-color: #CF682F}.heateor_sss_amp_pocket img{background-color: #ee4056}.heateor_sss_amp_polyvore img{background-color: #2A2A2A}.heateor_sss_amp_printfriendly img{background-color: #61D1D5}.heateor_sss_amp_protopage_bookmarks img{background-color: #413FFF}.heateor_sss_amp_pusha img{background-color: #0072B8}.heateor_sss_amp_qzone img{background-color: #2B82D9}.heateor_sss_amp_refind img{background-color: #1492ef}.heateor_sss_amp_rediff_mypage img{background-color: #D20000}.heateor_sss_amp_renren img{background-color: #005EAC}.heateor_sss_amp_segnalo img{background-color: #fdff88}.heateor_sss_amp_sina_weibo img{background-color: #ff0}.heateor_sss_amp_sitejot img{background-color: #ffc800}.heateor_sss_amp_skype img{background-color: #00AFF0}.heateor_sss_amp_sms img{background-color: #6ebe45}.heateor_sss_amp_slashdot img{background-color: #004242}.heateor_sss_amp_stumpedia img{background-color: #EDEDED}.heateor_sss_amp_svejo img{background-color: #fa7aa3}.heateor_sss_amp_symbaloo_feeds img{background-color: #6DA8F7}.heateor_sss_amp_telegram img{background-color: #3DA5f1}.heateor_sss_amp_trello img{background-color: #1189CE}.heateor_sss_amp_tuenti img{background-color: #0075C9}.heateor_sss_amp_twiddla img{background-color: #EDEDED}.heateor_sss_amp_typepad_post img{background-color: #2A2A2A}.heateor_sss_amp_viadeo img{background-color: #2A2A2A}.heateor_sss_amp_viber img{background-color: #8B628F}.heateor_sss_amp_wordpress img{background-color: #464646}.heateor_sss_amp_wykop img{background-color: #367DA9}.heateor_sss_amp_yahoo_mail img{background-color: #400090}.heateor_sss_amp_yahoo_messenger img{background-color: #400090}.heateor_sss_amp_youmob img{background-color: #3B599D}.heateor_sss_amp_gentlereader img{background-color: #46aecf}.heateor_sss_amp_threema img{background-color: #2A2A2A}.heateor_sss_amp_bluesky img{background-color:#0085ff}.heateor_sss_amp_threads img{background-color:#000}.heateor_sss_amp_raindrop img{background-color:#0b7ed0}.heateor_sss_amp_micro_blog img{background-color:#ff8800}.heateor_sss_amp amp-img{border-radius:999px;} .amp-logo amp-img{width:190px} .amp-menu input{display:none;}.amp-menu li.menu-item-has-children ul{display:none;}.amp-menu li{position:relative;display:block;}.amp-menu > li a{display:block;} /* Inline styles */ div.acss138d7{clear:both;}div.acssf5b84{--relposth-columns:3;--relposth-columns_m:2;--relposth-columns_t:2;}div.acssae964{aspect-ratio:1/1;background:transparent no-repeat scroll 0% 0%;height:150px;max-width:150px;}div.acss6bdea{color:#333333;font-family:Arial;font-size:12px;height:75px;}div.acsse3e3c{font-weight:bold;}amp-img.acss334b9{max-width:35px;} .icon-widgets:before {content: "\e1bd";}.icon-search:before {content: "\e8b6";}.icon-shopping-cart:after {content: "\e8cc";}
Обещания помогают сделать код чище, уменьшить количество зависимостей от внешних библиотек и подготовиться к async и await в ES7. Разработчики, которые ругают или не используют их, не знают, что они теряют.
Тем не менее, обещания могут быть сложными для понимания. Они очень отличаются от обычных коллбэков, к которым мы привыкли, а некоторые сюрпризы в синтаксисе могут доставить новичкам много проблем при отладке.
До появления обещаний разработчики JavaScript использовали функции обратного вызова. setTimeout, XMLHttpRequest, да и в основном все браузерные асинхронные функции основаны на коллбэках.
Чтобы продемонстрировать проблему с функциями обратного вызова, давайте сделаем некоторые анимации без HTML и CSS.
Допустим, мы хотим хотим сделать следующее:
Такой шаблон часто используется в CSS3 анимации. Давайте реализуем его с помощью нашего верного друга setTimeout. Код будет выглядеть примерно так:
runAnimation(0);
setTimeout(function() {
runAnimation(1);
setTimeout(function() {
runAnimation(2);
}, 1000);
}, 1000);7
Выглядит ужасно, не правда ли? А представьте себе на минуту, что вам нужно сделать 10 шагов, а не 3 — какую пирамиду из отступов вам придется построить. Это настолько плохо, что люди даже придумали специальное название — callback hell. И такие пирамиды из функций обратного вызова появляются везде — в обработке HTTP запросов, при работе с базой данных, при анимации, при реализации взаимодействия между процессами, и в других местах. Но:
Они не появляются в коде, который использует обещания.
Возможно, самый простой способ разобраться с тем, как работают обещания — сравнить их с коллбэками. Есть четыре основных отличия:
Коллбэки — это просто функции, которые выполняются в ответ на какое-либо событие, например, событие таймера или получение ответа от сервера. Любая функция может стать коллбэком, и любой коллбэк является функцией.
Обещания являются объектами, которые хранят информацию, произошли ли определенные события или нет, а если произошли — то и их результат.
Коллбэки определяются независимо от вызывающей функции и передаются в качестве аргументов. Вызывающая функция сохраняет коллбэк и вызывает его, когда происходит определенное событие.
Обещания создаются внутри асинхронных функций и возвращаются. Когда происходит событие, асинхронная функция обновляет обещание, чтобы уведомить об этом внешний мир.
Коллбэки, как правило, вызываются с информацией о том, успешно или неуспешно завершилась операция, и должны быть в состоянии обработать оба варианта.
Обещания ничего не обрабатывают по умолчанию, обработчики добавляются позже.
Коллбэки можно вызывать несколько раз в функциях, в которые они переданы.
Обещания могут представлять только одно событие — они обратывают либо успешное его завершение, либо неуспешное только один раз.
Имея это в виду, давайте рассмотрим обещания более детально.
Обещания ES6 являются экземплярами встроенного класса Promise, и создаются путем вызова new Promise с одной функцией в качестве аргумента. Например:
// Создание экземпляра обещания, который ничего не делает.
// Не волнуйтесь, мы рассмотрим этот момент подробнее.
promise = new Promise(function() {});
Вызов new Promise немедленно вызовет функцию, переданную в качестве аргумента. Цель этой функции состоит в информировании объекта Promise, когда событие, с которым он связан, будет завершено.
Для того, чтобы сделать это, функция, которую вы передаете в конструктор, может принимать два параметра, которые сами являются функциями — resolve и reject. Вызов resolve(value) пометит обещание как успешно завершенное и вызовет обработчик успешного завершения. Вызов reject(error) вызовет обработчик неуспешного завершения. Нельзя вызывать обе эти функции одновременно. Функции resolve и reject обе принимают один аргумент, который содержит в себе данные о событии.
Применим это к нашему примеру с анимацией. Приведенный выше пример использует функцию setTimeout, которая принимает коллбэк, — вместо этого мы хотим вернуть обещание. Конструктор new Promise позволяет нам это сделать:
/* Возвращаем обещание, которое резолвится через определенный промежуток времени */function delay(interval) {
return new Promise(function(resolve) {
setTimeout(resolve, interval);
});
}
var oneSecondDelay = delay(1000);
Отлично, теперь у нас есть обещание, которое резолвится через секунду. Я знаю, вам, вероятно, не терпится узнать, как сделать что-то по прошествии секунды — мы вернемся к этому позже, когда будем рассматривать вторую функцию promise.then.
Функция, которую мы передаем в new Promise в приведенном примере, принимает только параметр resolve, мы опустили параметр reject. Это потому, что setTimeout выполняется всегда и, таким образом, нет сценария, где мы он мог бы завершить неуспешно.
Допустим, мы хотим проверить, поддерживается ли определенная анимация браузером, и если анимация не поддерживается, узнать об этом заранее, а не после таймаута. Если функция isAnimationSupported(step) будет проверять поддерку, мы можем реализовать это с помощью reject:
function animationTimeout(step, interval) {
new Promise(function(resolve, reject) {
if (isAnimationSupported(step)) {
setTimeout(resolve, interval);
} else {
reject('animation not supported');
}
});
}
var firstKeyframe = animationTimeout(1, 1000);
Наконец, важно отметить, если в переданной функции возникнет исключение, обещание будет автоматически помечено как неуспешное. В качестве результата будет передан объект исключения, как если бы вы его передали функцию reject в качестве аргумента.
Чтобы лучше понять это, можно представить, что содержимое каждой функции, которую вы передаете в конструктор Promise, оборачивается в try/catch, например, так:
var promise = new Promise(function(resolve, reject) {
try {
// your code
}
catch (e) {
reject(e)
}
});
Так. Теперь вы понимаете, как создать обещание. Но когда оно у нас есть, как добавить обработчики событий успеха/неудачи? Для этого мы используем метод then.
Метод promise.then(onResolve, onReject) позволяет назначить обработчики событий обещания. В зависимости от аргументов, вы можете обработать событие успешного завершения, отказ, или оба:
// Только обработчик успеха
promise.then(function(details) {
// handle success
});
// Только обработчик отказа
promise.then(null, function(error) {
// handle failure
});
// Обработчики успеха и отказа
promise.then(
function(details) { /* handle success */ },
function(error) { /* handle failure */ }
);
Не пытайтесь обработать ошибки, возникающие в функции onResolve, в функции onError в том же вызове then. Это не работает.
// Это вызовет слезы и ужас
promise.then(
function() {
throw new Error('tears');
},
function(error) {
// Не будет вызван
console.log(error)
}
);
Если это все, что делает promise.then, то он действительно не имеет никаких преимуществ перед функциями обратного вызова. К счастью, это не так: обработчики, переданные в promise.then не просто обрабатывают результат предыдущего обещания — то, что они возвращают, передается в следующие обещание.
Это работает с числами, строками и другими типами:
delay(1000)
.then(function() {
return 5;
})
.then(function(value) {
console.log(value); // 5
});
Но что еще более важно, это работает с другими обещаниями — возвращение обещания из обработчика then передает это обещание в качестве возвращаемого значения then. Это позволяет реализовывать цепочки обещаний:
delay(1000)
.then(function() {
console.log('1 second elapsed');
return delay(1000);
})
.then(function() {
console.log('2 seconds elapsed');
});
И, как вы видите, цепочка обещаний позволяет избежать пирамиды из коллбэков. Независимо от того, как много уровней коллбэков, эквивалент на обещаниях будет плоским.
Можете ли вы теперь, используя то, о чем мы говорили выше, изменить пример с анимацией с помощью функции delay? Для вашего удобства я повторю первый пример. Чтобы проверить свое решение, наведите курсор мыши над пустым полем ниже.
runAnimation(0);
setTimeout(function() {
runAnimation(1);
setTimeout(function() {
runAnimation(2);
}, 1000);
}, 1000);
runAnimation(0);
delay(1000)
.then(function() {
runAnimation(1);
return delay(1000);
})
.then(function() {
runAnimation(2);
});
Какой из этих примеров вам проще понять? Версию с обещаниями, или версию с функциями обратного вызова? Дайте мне знать в комментариях!
До этого все было относительно просто, но есть несколько сложных моментов. Например:
Обработчик reject в функции promise.then возвращает успешно завершенное обещание.
Тот факт, что обработчики отказа возвращают по умолчанию успешное обещание, заставил меня помучаться, когда я только изучал эту тему — я не позволю этому случиться с вами. Вот пример того, за чем стоит следить:
new Promise(function(resolve, reject) {
reject(' :( ');
})
.then(null, function() {
// Handle the rejected promise
return 'some description of :(';
})
.then(
function(data) { console.log('resolved: '+data); },
function(error) { console.error('rejected: '+error); }
);
Что выведется в консоли? Проверьте ответ, наведя курсор мыши над полем ниже:
resolved: some description of :(
Если вы хотите обработать ошибку, возникающую в reject, убедитесь, что не просто возвращаете значение, а возвращаете отклоненное обещание. Т.е. вместо:
return 'some description of :('
Используйте волшебный прием, возвращающий отклоненное обещание с заданным значением:
// Я расскажу об этом позже return Promise.reject({anything: ‘anything’});
Кроме того, вы можете бросить исключение, при этом поможет тот факт, что
promise.then возвращает исключения в отклоненное обещание
Это означает, что вы можете в обработчике (успеха или неудачи) вернуть отклоненное обещание, сделав следующее:
throw new Error('some description of :(')
Имейте в виду, что, как и в функции, переданной в new Promise, любое исключение, брошенное в обработчиках, переданных в promise.then, будет возвращено в отклоненное обещание — вместо того, чтобы отобразиться в консоли, как вы могли бы ожидать. Из-за этого важно убедиться, что вы закончите всю цепочку только обработчиком reject — в противном случае вы можете потратить часы, пытаясь понять, где же ошибка (другое решение вы можете посмотреть в статье Are JavaScript Promises swallowing your errors? ).
Пример, демонстрирующий это решение:
delay(1000)
.then(function() {
throw new Error("oh no.");
})
.then(null, function(error) {
console.error(error);
});
Здесь все просто. promise.catch(handler) — это эквивалент promise.then(null, handler).
Нет, если серьезно, это не все, что он делает.
Один из паттернов — добавлять catch в конце каждой цепочки обещаний. Давайте вернемся к примеру с анимацией для демонстрации.
Допустим, у нас есть три шага анимации, с секундным отставанием между ними. Каждый шаг может бросить исключение, — например, из-за отсутствия поддержки браузером — после каждого then мы добавим блок catch, в котором сделаем нужные изменения, но без анимации.
Можете ли вы написать это, используя функцию delay, при условии, что на каждом шаге анимации можно назвать функцию runAnimation(number), а в качестве резерва можно вызвать runBackup(number)? Проверять нужно каждый шаг в отдельности, а не все, на случай, если бразуер все же может выполнить какие-то из шагов. Для проверки ответа наведите курсор на блок ниже.JavaScript
try {
runAnimation(0);
}
catch (e) {
runBackup(0);
}
delay(1000)
.then(function() {
runAnimation(1);
return delay(1000);
})
.catch(function() {
runBackup(1);
})
.then(function() {
runAnimation(2);
})
.catch(function() {
runBackup(2);
});
На сколько ваше решение похоже на мое? Если у вас есть вопросы, почему я сделал именно так, оставляйте комментарии!
В приведенном выше примере интересно сходство между блоком try/catch и обещаниями. Некоторые люди думают об общаниях как об отложенных блоках try/catch — я так не делаю, но думаю, это не повредит.
Итак, это три функции, которые вам нужно знать, чтобы использовать обещания. Но чтобы использовать из прекрасно, вы должны знать четвертую.
Функция Promise.all действительно удивительна. То, что доставляло столько боли при реализации на коллбэках, что я даже не решился привести пример, с ее помощью сделать очень просто.
Что она делает? Она возвращает обещание, которое успешно, если все аргументы успешны, и отклонен, когда любой из его аргументов отклонен. В случае успеха результирующее обещание содержит массив результатов каждого обещания, а в случае неудачи — ошибку первого неуспешного обещания.
Для чего это может быть полезно? Например, если мы хотим выполнить две анимации параллельно.
parallel animation 1 \
+ - subsequent animation
parallel animation 2 /
Вы можете поломать голову и сделать это на коллбэках. Или найти, скачать и подключить библиотеку, которая сделает это немного проще.
Или можете использовать Promise.all.
Допустим, у нас есть три функции, первые две parallelAnimation1() и parallelAnimation2() возвращают обещания, когда анимация завершится, а третья finalAnimation() должна вызваться, когда завершатся первые две. Реализовать такую логику мы можем следующим образом:JavaScript
Promise.all([
parallelAnimation1(),
parallelAnimation2()
]).then(function() {
finalAnimation();
});
Просто, не правда ли?
Другие случаи для использования Promise.all — загрузка нескольких HTTP-запросов одновременно, запуск нескольких процессов одновременно, или несколько одновременных запросов к базе данных. С Promise.all сделать это все легко.
Теперь, когда вы знаете четыре функции для работы с обещаниями, проверим ваши знания.
Ваша задача:
Функции скачивания первых двух файлов возвращают обещания, функция скачивания третьего файла, к сожалению, требует функцию обратного вызова.
Доступны следующие функции:
Вы можете проверить свои знания здесь, где я уже написал эти функции для вас.
Когда напишете свое решение, вы можете проверить его, наведя курсо мыши на блок ниже.JavaScript
function finalRequestPromise(options) {
return new Promise(function(resolve, reject) {
finalRequest(options, function(error, data) {
if (error) {
reject(error);
}
else {
resolve(data);
}
});
});
}
Promise
.all([initialRequestA(), initialRequestB()])
.then(function(results) {
var options = getOptionsFromInitialData(results[0], results[1]);
return finalRequestPromise(options);
})
.then(
function(file) { alert(file); },
function(error) { alert('ERROR: '+error); }
);
Теперь, когда вы знаете четыре функции, которые вам нужно знать, рассмотрим две функции, которые невероятно полезно знать.
Функция Promise.resolve возвращает обещание, которое успешно завершено с переданным значением. Эта функция является эквивалентом следующего кода:JavaScript
new Promise(function(resolve) {
resolve();
}).then(function() {
return value;
});
Функция Promise.reject(value) возвращает неуспешное обещание с переданным значением. Она является эквивалентом следующего кода:JavaScript
new Promise(function(resolve, reject) {
reject(value);
});
Это невероятно полезная функция, если вы хотите обработать ошибку, но не хотите возвращать успешное обещание после этого.
В Unity используется производительный язык программирования C#. Благодаря C# и Mono – кроссплатформенной реализации .NET,…