2016-08-09 9 views
-1

У меня есть много асинхронных методов для выполнения, и мои потоки программ могут сильно меняться в зависимости от каждого метода return. Логика ниже - один из примеров. Я не мог написать это легко читаемым способом с помощью обещаний. Как бы вы его написали?Избегайте обратного вызова javascript и обещайте ад

Пс: более сложные потоки приветствуются.

Ps2: is_business - это предопределенный флаг, где мы говорим, пишем ли мы «бизнес-пользователя» или «человека-пользователя».

begin transaction 
update users 
if updated 
    if is_business 
     update_business 
     if not updated 
      insert business 
     end if 
    else 
     delete business 
    end if 
else 
    if upsert 
     insert user 
     if is_business 
      insert business 
     end if 
    end if 
end if 
commit transaction 
+0

Я думаю, что это справедливый вопрос. Все эти операции предположительно асинхронны? – mooiamaduck

+0

Да @mooiamaduck. На самом деле я делаю вставки/обновления баз данных, которые являются асинхронными. – Eduardo

+0

Вы смотрите на 'koa.js' (или' co', если вы не пишете веб-приложение)? Если вы использовали koa и обещания, это выглядело бы очень точно так же, как вы его там.Или, если вы хотите использовать прямые обратные вызовы, вы можете использовать библиотеку управления потоком, такую ​​как 'async' (например, посмотрите на функцию' async.auto'. – Kevin

ответ

0

Раствора представляет собой смесь @mooiamaduck ответа и @Kevin комментария.

Использование обещаний, генераторов ES6 и библиотеки co делает код намного понятнее. Я нашел хороший пример при чтении примера библиотеки postgresql (pg). В приведенном ниже примере pool.connect и client.query являются асинхронными операциями, которые возвращают обещания. Мы можем легко добавить if/else после получения result, а затем выполнить более асинхронные операции, сохраняя код, похожий на синхронный.

co(function *() { 
    var client = yield pool.connect() 
    try { 
     yield client.query('BEGIN') 
     var result = yield client.query('SELECT $1::text as name', ['foo']) 
     yield client.query('INSERT INTO something(name) VALUES($1)', [result.rows[0].name]) 
     yield client.query('COMMIT') 
     client.release() 
    } catch(e) { 
    // pass truthy value to release to destroy the client 
    // instead of returning it to the pool 
    // the pool will create a new client next time 
    // this will also roll back the transaction within postgres 
    client.release(true) 
    } 
}) 
4

Хорошая вещь в обещаниях заключается в том, что они делают простую аналогию между синхронным кодом и асинхронным кодом. Для иллюстрации (с использованием библиотеки Q):

синхронный:

var thisReturnsAValue = function() { 
    var result = mySynchronousFunction(); 
    if(result) { 
    return getOneValue(); 
    } else { 
    return getAnotherValue(); 
    } 
}; 

try { 
    var value = thisReturnsAValue(); 
    console.log(value); 
} catch(err) { 
    console.error(err); 
} 

Asynchronous:

var Q = require('q'); 

var thisReturnsAPromiseForAValue = function() { 
    return Q.Promise(function() { 
    return myAsynchronousFunction().then(function(result) { 
     if(result) { 
     // Even getOneValue() would work here, because a non-promise 
     // value is automatically cast to a pre-resolved promise 
     return getOneValueAsynchronously(); 
     } else { 
     return getAnotherValueAsynchronously(); 
     } 
    }); 
    }); 
}; 

thisReturnsAPromiseForAValue().then(function(value) { 
    console.log(value); 
}, function(err) { 
    console.error(err); 
}); 

Вам просто нужно привыкнуть к мысли, что вернуть значения всегда доступны в качестве аргументов тогда -callbacks, и это обещание цепочки равнозначно составлению вызовов функций (f(g(h(x)))) или иным образом выполнение функций последовательно (var x2 = h(x); var x3 = g(x2);). Это по существу это! Вещи немного сложны, когда вы вводите ветви, но вы можете понять, что делать с этих первых принципов. Поскольку then-callbacks принимают обещания как возвращаемые значения, вы можете изменить значение, полученное асинхронно, возвращая еще одно обещание для асинхронной операции, которая разрешает новое значение на основе старого, а родительское обещание не будет разрешаться до тех пор, пока новый один решает! И, конечно же, вы можете вернуть эти обещания изнутри ветвей if-else.

Другая действительно хорошая вещь, проиллюстрированная в примере выше, заключается в том, что обещания (по крайней мере, совместимые с обещаниями/A +) обрабатывают исключения аналогичным образом. Первая ошибка «поднята» обходит обратные вызовы и пузырьки без ошибок до первого доступного обратного вызова ошибки, как и блок try-catch.

Для чего это стоит, я думаю, пытаясь имитировать это поведение с помощью обработанных вручную обратных вызовов типа Node.js, а библиотека async - это свой особый вид ада :).

После этих принципов код стал бы (предполагая, что все функции являются асинхронным и возвращать обещания):

beginTransaction().then(function() { 
    // beginTransaction() has run 
    return updateUsers(); // resolves the boolean value `updated` 
}).then(function(updated) { 
    // updateUsers() has "returned" `updated` 
    if(updated) { 
    if(isBusiness) { 
     return updateBusiness().then(function(updated) { 
     if(!updated) { 
      return insertBusiness(); 
     } 
     // It's okay if we don't return anything -- it will 
     // result in a promise which immediately resolves to 
     // `undefined`, which is a no-op, just like a missing 
     // else-branch 
     }); 
    } else { 
     return deleteBusiness(); 
    } 
    } else { 
    if(upsert) { 
     return insertUser().then(function() { 
     if(isBusiness) { 
      return insertBusiness(); 
     } 
     }); 
    } 
    } 
}).then(function() { 
    return commitTransaction(); 
}).done(function() { 
    console.log('all done!'); 
}, function(err) { 
    console.error(err); 
}); 
+0

Большое спасибо @mooiamaduck. Это именно то, чего я ожидал. Я понял, как это работает, но, к сожалению, по-моему, его все еще трудно читать. Я обнаружил, что сделать одну функцию обратного вызова и работать с флагами в коммутаторе/случае легче понять. – Eduardo

+0

Я считаю это более естественным, поэтому я полагаю, что это вопрос вкуса. – mooiamaduck

+0

Вещь, которая меня пугает, заключается в том, что код без обещаний должен быть превращен в код обещания для работы с другим кодом обещания. Это означает, что как только вы начнете использовать несколько обещаний, они будут кровоточить по всей вашей кодовой базе, пока почти все, что вы пишете, не будет основано на обещаниях. Это не обязательно плохо, но страшно думать о том, что произойдет, если вы попадете в тупик, где по какой-то причине код просто не может быть обещанием по какой-то причине. Может быть, это никогда не произойдет? –

Смежные вопросы