Я не помню, откуда я получил этот материал, но мог бы лучше понять обещания.
Обещания не являются обратными вызовами. Обещание представляет собой будущий результат асинхронной операции. Конечно, записывая их так, как вы, вы получаете небольшую выгоду. Но если вы пишете их так, как они предназначены для использования, вы можете написать асинхронный код таким образом, который напоминает синхронный код, и его гораздо проще выполнить: ПРЕИМУЩЕСТВА 1. Читаемость по обратным вызовам 2. Легко ловить ошибки. 3. Одновременные обратные вызовы
ПРЕИМУЩЕСТВА Обещания обеспечивают более сжатый и понятный способ представления последовательных асинхронных операций в javascript. Они представляют собой отличный синтаксис для достижения того же эффекта, что и обратные вызовы. Преимуществом является повышенная читаемость. Что-то вроде этого
aAsync()
.then(bAsync)
.then(cAsync)
.done(finish);
гораздо читаемо, чем эквивалент передачи каждой из этих отдельных функций в качестве обратных вызовов, таких как
Async(function(){ return bAsync(function(){ return cAsync(function(){ finish() }) }) });
//-------------------------------------------- api().then(function(result){
return api2(); }).then(function(result2){ return api3(); }).then(function(result3){ // do work });
2. Легко ловить ошибки. Конечно, не намного меньше кода, но гораздо более удобочитаемым. Но это еще не конец. Давайте обнаружим истинные преимущества: что, если вы хотите проверить какую-либо ошибку на любом из шагов? Было бы чертовски сделать это с помощью обратных вызовов, но с обещаниями - кусок торта:
api().then(function(result){
return api2(); }).then(function(result2){ return api3(); }).then(function(result3){ // do work }).catch(function(error) {
//handle any error that may occur before this point });
/* Pretty much the same as a try { ... } catch block.
Even better: */
api().then(function(result){ return api2(); }).then(function(result2){ return api3(); }).then(function(result3){ // do work }).catch(function(error) { //handle any error that may occur before this point }).then(function() { //do something whether there was an error or not //like hiding an spinner if you were performing an AJAX request. });
2. Легко ловить ошибки. Что делать, если эти 3 вызова api, api2, api3 могут выполняться одновременно (например, если они были вызовами AJAX), но вам нужно было дождаться трех? Без обещаний вам нужно создать какой-то счетчик. С обещаниями, использующими нотацию ES6, это еще один кусок торта и довольно аккуратный:
Promise.all([api(), api2(), api3()]).then(function(result) { //do work. result is an array contains the values of the three fulfilled promises. }).catch(function(error) { //handle the error. At least one of the promises rejected. });
Надеюсь, вы увидите обещания в новом свете.
Насколько я могу судить, это ошибка clang.
Инициализация списка копирования имеет довольно неинтуитивное поведение: он считает явные конструкторы жизнеспособными до тех пор, пока разрешение перегрузки не будет полностью завершено, но может отклонить результат перегрузки, если выбран явный конструктор. Формулировка в проекте post-N4567 [over.match.list] p1
В инициализации списка копий, если выбран конструктор
blockquote>explicit
, инициализация плохо сформирована. [ Примечание: Это отличается от других ситуаций (13.3.1.3, 13.3.1.4), где рассматриваются только конструкторы преобразования для инициализации копирования. Это ограничение применяется только в том случае, если эта инициализация является частью окончательного результата разрешения перегрузки. - end note ]
clang HEAD принимает следующую программу:
#include <iostream> using namespace std; struct String1 { explicit String1(const char*) { cout << "String1\n"; } }; struct String2 { String2(const char*) { cout << "String2\n"; } }; void f1(String1) { cout << "f1(String1)\n"; } void f2(String2) { cout << "f2(String2)\n"; } void f(String1) { cout << "f(String1)\n"; } void f(String2) { cout << "f(String2)\n"; } int main() { //f1( {"asdf"} ); f2( {"asdf"} ); f( {"asdf"} ); }
Которая, за исключением комментирования звоните в
f1
, прямо из N2532 Bjarne Stroustrup - Унифицированная инициализация , глава 4. Благодаря Johannes Schaub для показа мне эту статью на std-обсуждение .В той же главе содержится следующее объяснение:
Реальное преимущество
blockquote>explicit
в том, что оно делает ошибкуf1("asdf")
ошибкой. Проблема в том, что разрешение перегрузки «предпочитает» конструкторы неexplicit
, так чтоf("asdf")
вызываетf(String2)
. Я считаю, что разрешениеf("asdf")
меньше идеала, потому что авторString2
, вероятно, не имел целью разрешить двусмысленности в пользуString2
(по крайней мере, не в каждом случае, когда явные и неявные конструкторы происходят так) и писательString1
, конечно, этого не делал. Правило благоприятствует «неаккуратным программистам», которые не используютexplicit
.
Насколько мне известно, N2640 - Списки инициализаторов - Альтернативный механизм и обоснование - последняя статья, которая включает в себя обоснование такого разрешения перегрузки; его преемник N2672 был проголосован в черновик C ++ 11.
Из его главы «Значение явного»:
Первый подход для того чтобы сделать пример плохо сформированным, требуется, чтобы все конструкторы (явные и неявные) рассматривались для неявных преобразований, но если выбран явный конструктор, эта программа плохо сформирована. Это правило может ввести свои собственные сюрпризы; например:
struct Matrix { explicit Matrix(int n, int n); }; Matrix transpose(Matrix); struct Pixel { Pixel(int row, int col); }; Pixel transpose(Pixel); Pixel p = transpose({x, y}); // Error.
Второй подход заключается в том, чтобы игнорировать явные конструкторы при поиске жизнеспособности неявного преобразования, но включать их при фактическом выборе конструктора преобразования: если явный конструктор заканчивается выбранный, программа плохо сформирована. Этот альтернативный подход позволяет последнему примеру (Pixel-vs-Matrix) работать как ожидалось (выбран
blockquote>transpose(Pixel)
), в то время как исходный пример («X x4 = { 10 };
») плохо сформирован.Хотя в этом документе предлагается использовать второй подход, его формулировка представляется ошибочной - в моей интерпретации формулировки она не приводит к поведению, изложенному в обоснованной части статьи. Формулировка пересмотрена в N2672, чтобы использовать первый подход, но я не мог найти никакого обсуждения о том, почему это было изменено.
Конечно, немного больше формулировок, связанных с инициализацией переменной, как в OP, но учитывая разницу в поведении между clang и gcc, то же самое для первой программы-образца в моем ответе, я думаю, что это касается основных моментов.
Это не полный ответ, хотя он слишком длинный, как комментарий. Я постараюсь предложить контрпример к вашим рассуждениям, и я готов смотреть вниз, потому что я далек от того, чтобы быть уверенным. В любом случае, давайте попробуем! : -)
Это следует из приведенного примера:
struct A {
A(int, int) { }
};
struct B {
B(A) { }
explicit B(int, int ) { }
};
int main() {
B paren({1, 2});
}
В этом случае утверждение {1, 2}
, по-видимому, дает два решения:
B(A)
, поскольку A(int, int)
не является явным и, следовательно, это разрешено, и это фактически первый кандидат B{B(A{1,2})}
(ну, позвольте мне оскорбить нотацию, чтобы дать вам представление и что я имею в виду), то есть {1,2}
позволяет построить временный объект B
, который используется сразу после аргумента для конструктора copy / move, и это снова разрешено, потому что задействованные конструкторы не являются явными . Последний объясняет второй и третий кандидаты.
Имеет ли смысл? Я готов удалить ответы до тех пор, пока вы объясните мне, что не так в моих рассуждениях. : -)