Я разработал небольшую библиотеку для Dynamics CRM REST/ODATA webservice (CrmRestKit). Библиотека зависит от jQuery и использует шаблон обещания, в частности, перспективный шаблон jQuery.
Теперь мне нравится переносить эту библиотеку на bluebird и удалять зависимость jQuery. Но я столкнулся с проблемой, потому что bluebird не поддерживает синхронное разрешение объектов-обещаний.
Некоторая контекстная информация:
API CrmRestKit исключает необязательный параметр, который определяет, должен ли вызов веб-службы выполняться в режиме синхронизации или асинхронного режима:
CrmRestKit.Create( 'Account', { Name: "foobar" }, false ).then( function ( data ) {
....
} );
Когда вы передаете "true" или опускаете последний параметр, будет ли метод синхронизировать запись. Режим.
Иногда необходимо выполнить операцию в режиме синхронизации, например, вы можете написать код JavaScript для Dynamics CRM, который был включен для события сохранения формы, и в этом обработчике событий вам необходимо выполнить синхронизацию для проверки (например, подтвердите, что существует определенное количество дочерних записей, в случае наличия правильного количества записей, отмените операцию сохранения и покажите сообщение об ошибке).
Теперь моя проблема заключается в следующем: bluebird не поддерживает разрешение в синхронном режиме. Например, когда я делаю следующее, обработчик "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-event немедленно вызывается при применении к уже разрешенному объекту-обещанию.
Когда я делаю то же самое (используйте тогда по уже разрешенному обещанию) с 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 работать одинаково?
Update: Предоставленный код был просто для иллюстрации проблемы. Идея lib: Независимо от режима исполнения (sync, async) вызывающий абонент всегда будет обрабатывать объект-обещание.
Относительно "... запроса пользователя... кажется, не имеет никакого смысла": когда вы предоставляете два метода "CreateAsync" и "CreateSync", это также зависит от пользователя, чтобы решить, как выполняется операция,
В любом случае с текущей реализацией поведение по умолчанию (последний параметр является необязательным) - это асинхронное выполнение. Таким образом, для 99% кода требуется объект-обещание, необязательный параметр используется только для 1% случаев, когда вам просто нужно выполнить синхронизацию. Кроме того, я разработал для lib для себя, и я использую в 99,9999% случаев асинхронный режим, но я подумал, что неплохо иметь возможность идти по синхронной дороге, как вам нравится.
Но я думаю, что я понял, что метод синхронизации должен просто вернуть значение. Для следующего выпуска (3.0) я буду использовать "CreateSync" и "CreateAsync".
Спасибо за ваш вклад.
Update-2 Мое намерение для необязательного параметра состояло в том, чтобы обеспечить согласованное поведение и предотвратить логическую ошибку. Предположим, что вы как потребитель моего метода "GetCurrentUserRoles", который использует lib. Таким образом, метод всегда будет давать обещание, это означает, что вам нужно использовать метод "then" для выполнения кода, который зависит от результата. Поэтому, когда кто-то пишет такой код, я согласен, что это абсолютно неправильно:
var currentUserRoels = null;
GetCurrentUserRoles().then(function(roles){
currentUserRoels = roles;
});
if( currentUserRoels.indexOf('foobar') === -1 ){
// ...
}
Я согласен, что этот код сломается, когда метод GetCurrentUserRoles изменится с синхронизации на асинхронный.
Но я понимаю, что это я не очень хороший дизайн, потому что потребитель должен теперь, когда он занимается асинхронным методом.