Reputation: 718
Promises are great for attaching something to the end of an async call, without the async call needing to be aware of its followers. Can promises be used in more general or complex situations where callbacks are needed in the middle of an async call, and if so can it provide the same level of separation?
For example, suppose you have two functions a & b where the execution order transfers from a to b and back twice.
Using callbacks:
function a() {
console.log('a1');
b(function(b2) {
requestAnimationFrame(function() {
console.log('a2');
b2();
});
});
}
function b(a2) {
console.log('b1');
a2(function() {
requestAnimationFrame(function() {
console.log('b2');
});
});
}
I'm aware that the two functions could be each broken into two pieces and strung together with promises, but that might lose important expensive-to-compute scope variables setup in a1/b1, or lead to redundant code or even harder to follow execution order.
The question is, can this code be rewritten using promises in such a way that b() doesn't need to know about a(), but does offer the caller a chance to interject something before b() is done executing?
Upvotes: 3
Views: 954
Reputation: 42500
Before answering, I've modified your example slightly to highlight execution flow:
function a() {
console.log('a1');
var c = function(b2) {
var d = function() {
console.log('a2');
b2();
});
requestAnimationFrame(d);
}
b(c);
}
function b(a2) {
console.log('b1');
var e = function() {
var f = function() {
console.log('b2');
});
requestAnimationFrame(f);
}
a2(e);
}
This is the same code, except I've named the four anonymous functions c
, d
, e
and f
, because I want to refer to them.
Here's the order of execution:
a
is calleda
calls b
synchronouslyb
calls c
synchronouslyc
calls requestAnimationFrame
(an async API)requestAnimationFrame
returnsc
returnsb
returns and is done executinga
returns and is done executing.Later, when an animation frame becomes available, this happens:
d
is calledd
calls e
synchronouslye
calls requestAnimationFrame
Of course e
has access to variables defined in b
which outlive the execution of the original b
thanks to JavaScript closures.
Likewise, in JavaScript, we view a
as an asynchronous operation that ends once everything a
initiates is done, but there are only two asynchronous steps (4 and 11) in it, so those two would benefit from promises.
Now onto your question: Can promises provide the same result and separation, without b knowing about a? Yes, but it wont be a fair comparison to your example, because it'll bulk up your synchronous calls to be asynchronous when it's unneeded:
function a() {
console.log('a1');
var b2;
b().then(function(result) {
b2 = result;
return new Promise(function(r) { requestAnimationFrame(r); })
}).then(function() {
console.log('a2');
return b2();
}).catch(function(e) {
console.log(e.message); // error-handling
});
}
function b() {
console.log('b1');
return Promise.resolve(function() {
return new Promise(function(r) {
requestAnimationFrame(r);
}).then(function() {
console.log('b2');
});
});
}
The trick here is that b returns steps to execute later in a, which is a pattern that can be repeated.
Is this a good pattern? Hard to say without knowing more about the original use-case. For asynchronous calls I have yet to meet one that didn't benefit from promises, but for synchronous calls I wouldn't, since it alters execution order (unless maybe there's just one outlier and all the rest are asynchronous, and it helps readability).
Upvotes: 3