Я разработал небольшую библиотеку для веб-службы Dynamics CRM REST/ODATA (CrmRestKit). Библиотека зависит от jQuery и использует шаблон обещания, соответственно шаблон, похожий на обещание jQuery.
Теперь мне нравится портировать эту библиотеку на bluebird и удалять зависимость от jQuery. Но я столкнулся с проблемой, потому что bluebird не поддерживает синхронное разрешение объектов-обещаний.
Некоторая контекстная информация:
API CrmRestKit исключает необязательный параметр, который определяет, должен ли вызов веб-службы выполняться в синхронном или асинхронном режиме:
CrmRestKit.Create( 'Account', { Name: "foobar" }, false ).then( function ( data ) {
....
} );
Когда вы передаете «true» или опускаете последний параметр, метод создает запись синхронно. режим.
Иногда необходимо выполнить операцию в режиме синхронизации, например, вы можете написать код JavaScript для Dynamics CRM, который задействуется для события сохранения формы, и в этом обработчике события вам необходимо выполнить операцию синхронизации для проверки ( например, проверить наличие определенного количества дочерних записей, если существует нужное количество записей, отменить операцию сохранения и показать сообщение об ошибке).
Моя проблема сейчас следующая: bluebird не поддерживает разрешение в sync-режиме. Например, когда я делаю следующее, обработчик then вызывается асинхронно:
function print( text ){
console.log( 'print -> %s', text );
return text;
}
///
/// 'Promise.cast' cast the given value to a trusted promise.
///
function getSomeTextSimpleCast( opt_text ){
var text = opt_text || 'Some fancy text-value';
return Promise.cast( text );
}
getSomeTextSimpleCast('first').then(print);
print('second');
Вывод следующий:
print -> second
print -> first
Я ожидаю, что «второй» появится после «первого», потому что обещание уже разрешено со значением. Поэтому я бы предположил, что обработчик события then вызывается немедленно при применении к уже разрешенному объекту обещания.
Когда я сделаю то же самое (использую then для уже разрешенного обещания) с jQuery, я получу ожидаемый результат:
function jQueryResolved( opt_text ){
var text = opt_text || 'jQuery-Test Value',
dfd = new $.Deferred();
dfd.resolve(text);
// return an already resolved promise
return dfd.promise();
}
jQueryResolved('third').then(print);
print('fourth');
Это приведет к следующему результату:
print -> third
print -> fourth
Есть ли способ заставить bluebird работать таким же образом?
Обновление: предоставленный код предназначен только для иллюстрации проблемы. Идея библиотеки такова: независимо от режима выполнения (синхронный, асинхронный) вызывающая сторона всегда будет иметь дело с объектом обещания.
Что касается «... спрашивать пользователя ... не имеет смысла»: когда вы предоставляете два метода «CreateAsync» и «CreateSync», пользователь также должен решить, как выполняется операция.
В любом случае, в текущей реализации поведение по умолчанию (последний параметр является необязательным) является асинхронным выполнением. Таким образом, 99% кода требует объекта обещания, необязательный параметр используется только для 1% случаев, когда вам просто нужно выполнение синхронизации. Кроме того, я разработал lib для себя и использую в 99,9999% случаев асинхронный режим, но я подумал, что неплохо иметь возможность идти по пути синхронизации, как вам нравится.
Но я думаю, что понял, что метод синхронизации должен просто возвращать значение. В следующем выпуске (3.0) я реализую «CreateSync» и «CreateAsync».
Спасибо за ваш вклад.
Обновление-2. Мое намерение в отношении необязательного параметра состояло в том, чтобы обеспечить согласованное поведение и предотвратить логическую ошибку. Предположим, что вы являетесь потребителем моего метода «GetCurrentUserRoles», который использует lib. Таким образом, метод всегда будет возвращать обещание, а это означает, что вам нужно использовать метод «тогда» для выполнения кода, зависящего от результата. Поэтому, когда кто-то пишет такой код, я согласен, что это совершенно неправильно:
var currentUserRoels = null;
GetCurrentUserRoles().then(function(roles){
currentUserRoels = roles;
});
if( currentUserRoels.indexOf('foobar') === -1 ){
// ...
}
Я согласен, что этот код сломается, когда метод GetCurrentUserRoles изменится с синхронизации на асинхронность.
Но я понимаю, что это не очень хороший дизайн, потому что теперь потребитель должен иметь дело с асинхронным методом.
CrmRestKit.CreateAsync
иCrmRestKit.CreateSync
вместоCrmRestKit.Create({async: ?})
. Это полностью отличается от функции, которая иногда может знать свое возвращаемое значение синхронно (например, кеш 1-го уровня для вызова базы данных). - person Esailija   schedule 16.01.2014CrmRestKit.Create(..., sync)
) не имеет смысла? Если да, то я согласен. - person Denys Séguret   schedule 16.01.2014CreateSync()
, не имеет смысла возвращать обещание, поскольку это обычный синхронный вызов. Та же перегрузка параметров, которую использует OP, используется вXMLHttpRequest
, которую можно заменить наopenAsync()
иopenSync()
, но ваш исходный комментарий ошибочно использует метод, который иногда возвращается синхронно. Это не то же самое, потому что перегрузка, в которой async = false всегда, возвращает синхронно, поэтому в этом случае нет необходимости в обещании. - person Esailija   schedule 16.01.2014.responseText
напрямую. - person Esailija   schedule 16.01.2014