#jquery #asynchronous #monads #jquery-deferred
#jquery #асинхронный #монады #jquery-отложенный
Вопрос:
Вдохновленный этим (превосходным) обсуждением использования Promises в javascript, я пытаюсь понять, как я мог бы использовать Deferred для объединения в цепочку асинхронных и неасинхронных функций, чтобы избежать уплаты налога на обратный вызов при использовании моего кода «Глобального хранилища«.
У меня есть несколько вопросов, связанных с этим, но я задам их здесь вместе, потому что контекст тот же.
Единственное, чего я не могу понять, — это как я могу создать отложенный из чего-то, что не является асинхронным, то есть как мне взять значение, обернуть его в обещание и вернуть напрямую? ( a -> M<a>
)
Кроме того, как я могу взять асинхронную функцию и обернуть ее так, чтобы она возвращала свой результат напрямую, но завернутый в обещание? ( (a -> b) -> (a -> M<b>)
)
Последний вопрос, для монадических фриков — есть ли стандартное имя для этой функции? [a] -> (a -> M<b>) -> M<[b]>
Комментарии:
1. Для всех, кто попал сюда, пытаясь разобраться, как «связать» отложенные объекты, я придумал (как мне кажется) элегантное решение: jsfiddle.net/Benjol/DjrRD
Ответ №1:
Обернуть значение в обещание так же просто, как использовать $.когда:
var promise = $.when( value );
Кроме того, начиная с jQuery 1.6, у вас есть очень простой метод цепочки (pipe):
var chained = functionThatReturnsAPromise().pipe(function( resolveValue ) {
return functionThatReturnsAnotherPromise( resolveValue );
});
chained.done(function() {
// Both asynchronous operations done in sequence
});
Надеюсь, это поможет.
Комментарии:
1. Очень приятно, спасибо. Так получилось, что я адаптировал его для here, где они еще не обновились до 1.6 (AFAIK), но $.when удобен.
2. Для тех, кому любопытно,
$.when(value)
является сокращением для$.Deferred().resolve(value).promise()
Ответ №2:
Я думаю, что способ, которым вы могли бы превратить значение в обещание, — это просто «предварительно выполнить» отложенный:
function v2p(value) {
var rv = $.Deferred();
rv.resolveWith(null, [value]);
return rv.promise();
}
Теперь передача функции в «.done()» приведет к немедленному вызову функции со значением.
v2p("hello").done(function(value) { alert(value); });
немедленно предупредил бы «привет».
Комментарии:
1. Отличное начало, спасибо! Я отредактировал ваш ответ, чтобы запустить его, надеюсь, он не изменил то, что вы имели в виду.
2. Ах да, верно; извините за это. Я пока только с трудом играл с этим отложенным материалом, но это кажется действительно классным 🙂
Ответ №3:
С помощью @Pointy реализация ‘lift’ становится тривиальной:
function unit(value) {
var rv = $.Deferred();
rv.resolveWith(null, [value]);
return rv.promise();
}
function lift(fn) {
return function(x) {
return unit(fn(x));
};
}
lift(alert)("hello");
function bind(fn) {
return function(x) {
return x.done(function(y) { return fn(y); });
}
}
function compose(f, g) { return function(x) { g(f(x)); } };
function twice(x) { return 2 * x; }
var alert2 = compose(bind(lift(twice)), bind(lift(alert)));
alert2(unit(4)); //error at the end because alert doesn't return any values
Теперь мне просто нужно решить, как реализовать [a] -> (a -> M<b>) -> M<[b]>
и как это назвать!
РЕДАКТИРОВАТЬ, вместо этого я закончил реализацию (a -> M<b>) -> ([a] -> M<[b]>)
, это выглядит так:
function listBind(fn) {
return function(a) {
var Mb = $.Deferred();
var b = [], pending = a.length;
var callback = function(i,val) {
b[i] = val;
if(--pending == 0) Mb.resolve(b);
};
for(var i = 0, n = a.length; i < n; i ) {
(function(closure) { //ugly, but have to use closure to 'copy' i
fn(a[closure]).done(function(val) { callback(closure, val); })
})(i);
}
return Mb.promise();
};
}
Итак, учитывая функцию, которая получает один отложенный элемент, эта функция listBind возвращает новую функцию, которая принимает массив значений и использует их для возврата другого списка значений внутри отложенного элемента.