Promise callbacks returning promises - javascript

With regard to these great two sources: NZakas - Returning Promises in Promise Chains and MDN Promises, I would like to ask the following:
Each time that we return a value from a promise fulfillment handler, how is that value passed to the new promise returned from that same handler?
For instance,
let p1 = new Promise(function(resolve, reject) {
resolve(42);
});
let p2 = new Promise(function(resolve, reject) {
resolve(43);
});
let p3 = p1.then(function(value) {
// first fulfillment handler
console.log(value); // 42
return p2;
});
p3.then(function(value) {
// second fulfillment handler
console.log(value); // 43
});
In this example, p2 is a promise. p3 is also a promise originating from p1's fulfillment handler. However p2 !== p3. Instead p2 somehow magically resolves to 43 (how?) and that value is then passed to p3's fulfillment handler. Even the sentence here is confusing.
Could you please explain to me what exactly is going on here? I am totally confused over this concept.

Let’s say that throwing inside then() callback rejects the result promise with a failure, and returning from then() callback fulfills the result promise with a success value.
let p2 = p1.then(() => {
throw new Error('lol')
})
// p2 was rejected with Error('lol')
let p3 = p1.then(() => {
return 42
})
// p3 was fulfilled with 42
But sometimes, even inside the continuation, we don’t know whether we have succeeded or not. We need more time.
return checkCache().then(cachedValue => {
if (cachedValue) {
return cachedValue
}
// I want to do some async work here
})
However, if I do async work there, it would be too late to return or throw, wouldn’t it?
return checkCache().then(cachedValue => {
if (cachedValue) {
return cachedValue
}
fetchData().then(fetchedValue => {
// Doesn’t make sense: it’s too late to return from outer function by now.
// What do we do?
// return fetchedValue
})
})
This is why Promises wouldn’t be useful if you couldn’t resolve to another Promise.
It doesn’t mean that in your example p2 would become p3. They are separate Promise objects. However, by returning p2 from then() that produces p3 you are saying “I want p3 to resolve to whatever p2 resolves, whether it succeeds or fails”.
As for how this happens, it’s implementation-specific. Internally you can think of then() as creating a new Promise. The implementation will be able to fulfill or reject it whenever it likes. Normally, it will automatically fulfill or reject it when you return:
// Warning: this is just an illustration
// and not a real implementation code.
// For example, it completely ignores
// the second then() argument for clarity,
// and completely ignores the Promises/A+
// requirement that continuations are
// run asynchronously.
then(callback) {
// Save these so we can manipulate
// the returned Promise when we are ready
let resolve, reject
// Imagine this._onFulfilled is an internal
// queue of code to run after current Promise resolves.
this._onFulfilled.push(() => {
let result, error, succeeded
try {
// Call your callback!
result = callback(this._result)
succeeded = true
} catch (err) {
error = err
succeeded = false
}
if (succeeded) {
// If your callback returned a value,
// fulfill the returned Promise to it
resolve(result)
} else {
// If your callback threw an error,
// reject the returned Promise with it
reject(error)
}
})
// then() returns a Promise
return new Promise((_resolve, _reject) => {
resolve = _resolve
reject = _reject
})
}
Again, this is very much pseudo-code but shows the idea behind how then() might be implemented in Promise implementations.
If we want to add support for resolving to a Promise, we just need to modify the code to have a special branch if the callback you pass to then() returned a Promise:
if (succeeded) {
// If your callback returned a value,
// resolve the returned Promise to it...
if (typeof result.then === 'function') {
// ...unless it is a Promise itself,
// in which case we just pass our internal
// resolve and reject to then() of that Promise
result.then(resolve, reject)
} else {
resolve(result)
}
} else {
// If your callback threw an error,
// reject the returned Promise with it
reject(error)
}
})
Let me clarify again that this is not an actual Promise implementation and has big holes and incompatibilities. However it should give you an intuitive idea of how Promise libraries implement resolving to a Promise. After you are comfortable with the idea, I would recommend you to take a look at how actual Promise implementations handle this.

Basically p3 is return-ing an another promise : p2. Which means the result of p2 will be passed as a parameter to the next then callback, in this case it resolves to 43.
Whenever you are using the keyword return you are passing the result as a parameter to next then's callback.
let p3 = p1.then(function(value) {
// first fulfillment handler
console.log(value); // 42
return p2;
});
Your code :
p3.then(function(value) {
// second fulfillment handler
console.log(value); // 43
});
Is equal to:
p1.then(function(resultOfP1) {
// resultOfP1 === 42
return p2; // // Returning a promise ( that might resolve to 43 or fail )
})
.then(function(resultOfP2) {
console.log(resultOfP2) // '43'
});
Btw, I've noticed that you are using ES6 syntax, you can have a lighter syntax by using fat arrow syntax :
p1.then(resultOfP1 => p2) // the `return` is implied since it's a one-liner
.then(resultOfP2 => console.log(resultOfP2));

In this example, p2 is a promise. p3 is also a promise originating from p1's fulfillment handler. However p2 !== p3. Instead p2 somehow magically resolves to 43 (how?) and that value is then passed to p3's fulfillment handler. Even the sentence here is confusing.
a simplified version how this works (only pseudocode)
function resolve(value){
if(isPromise(value)){
value.then(resolve, reject);
}else{
//dispatch the value to the listener
}
}
the whole thing is quite more complicated since you have to take care, wether the promise has already been resolved and a few more things.

I'll try to answer the question "why then callbacks can return Promises themselves" more canonical. To take a different angle, I compare Promises with a less complex and confusing container type - Arrays.
A Promise is a container for a future value.
An Array is a container for an arbitrary number of values.
We can't apply normal functions to container types:
const sqr = x => x * x;
const xs = [1,2,3];
const p = Promise.resolve(3);
sqr(xs); // fails
sqr(p); // fails
We need a mechanism to lift them into the context of a specific container:
xs.map(sqr); // [1,4,9]
p.then(sqr); // Promise {[[PromiseValue]]: 9}
But what happens when the provided function itself returns a container of the same type?
const sqra = x => [x * x];
const sqrp = x => Promise.resolve(x * x);
const xs = [1,2,3];
const p = Promise.resolve(3);
xs.map(sqra); // [[1],[4],[9]]
p.then(sqrp); // Promise {[[PromiseValue]]: 9}
sqra acts like expected. It just returns a nested container with the correct values. This is obviously not very useful though.
But how can the result of sqrp be interpreted? If we follow our own logic, it had to be something like Promise {[[PromiseValue]]: Promise {[[PromiseValue]]: 9}} - but it is not. So what magic is going on here?
To reconstruct the mechanism we merely need to adapt our map method a bit:
const flatten = f => x => f(x)[0];
const sqra = x => [x * x];
const sqrp = x => Promise.resolve(x * x);
const xs = [1,2,3];
xs.map(flatten(sqra))
flatten just takes a function and a value, applies the function to the value and unwraps the result, thus it reduces a nested array structure by one level.
Simply put, then in the context of Promises is equivalent to map combined with flatten in the context of Arrays. This behavior is extremely important. We can apply not only normal functions to a Promise but also functions that itself return a Promise.
In fact this is the domain of functional programming. A Promise is a specific implementation of a monad, then is bind/chain and a function that returns a Promise is a monadic function. When you understand the Promise API you basically understand all monads.

Related

Why does a second reference to a promise not resolve in Promise.all?

I'm attempting to assign a reference to a Promise, prior to its resolution, to a variable. I had expected that when the promise resolves in Promise.all, the reference to that same promise would be resolved too but instead it's still a promise object, not the resolved value
function returnPromise(out) {
return new Promise((resolve, reject) => {
setTimeout(() => {
return resolve(out);
}, 1000);
})
}
let test1;
let promiseStack = [];
for (let i = 0; i < 10; i++) {
let prom = returnPromise(i);
promiseStack.push(prom);
if (i === 1) {
test1 = prom; // Set 1 of the references to a promise to a variable
}
}
Promise.all(promiseStack).then((results) => {
console.log(results); // An array of 10 values (0 to 9)
console.log(test1); // A promise object - expected this to be = 1
})
My question is, why does the test1 value not appear as resolved in the Promise.all block?
test1 = prom; // Set 1 of the references to a promise to a variable
You assign a promise to test1.
You never assign another value to test1.
When you examine test1 with:
console.log(test1); // A promise object - expected this to be = 1
… it is still a promise.
You only see the resolved value of a promise when you do something that explicitly examines the resolved value of it (e.g. look at the argument passed to a function you pass to then).
The promise object itself is never transformed into the resolved value.
test1.then(value => console.log(value)); // This will be 1
My question is, why does the test1 value not appear as resolved in the Promise.all block?
It does. It's a resolved promise at that point (and it shows as such in Chrome's devtools; you can't directly observe that state in code, though). Nothing in your code updates the test1 variable after you assign it the promise instance, so naturally it still refers to the promise instance (a resolved one). Promises don't (and can't) go back and update all the variables that point to them to change them to the resolved value.

Returning values from Javascript Promise chain

Modern JS/Promise newbie here. I'm having trouble finding the answer to a simple question, despite the overwhelming amount of content on this subject.
I believe I have a decent understanding of JS Promises (thanks to various sources, including mdn and https://spin.atomicobject.com/2016/02/16/how-javascript-promises-work/)
I have consumed and produced Promises regularly in their simplest form, however I keep stumbling on methods in my inherited projects with the following pattern:
const doAsyncOp = () =>
$http.get(uri)
.then(
value => value,
reject => //...
);
My big question is: What happens when you simply return a value from your success handler? I need to consume this method, and need access to 'value' in the client code. Is this technically unhandled? Should I rewrite the implementation?
My big question is: What happens when you simply return a value from your success handler?
When you return a value from a .then() handler, that value becomes the resolved value of the parent promise chain: So, in this code:
// utility function that returns a promise that resolves after a delay
function delay(t, v) {
return new Promise(resolve => {
setTimeout(resolve.bind(null, v), t);
});
}
function test() {
return delay(100, 5).then(val => {
// this return value becomes the resolved value of the promise chain
// that is returned from this function
return 10;
});
}
test().then(result => {
// logs 10
console.log(result);
});
When you run it, it will log 10 because of the return 10 in the .then() handler.
There are four possibilities from a .then() handler:
Return a regular value such as return 10 or return val. That value becomes the resolved value of the promise chain. If no value is returned (which in Javascript means the return value is undefined), then the resolved value of the promise chain is undefined.
Return a promise that ultimately resolves or is already resolved. This promise is added to the chain and the promise chain takes on the resolved value of that promise.
Return a promise that ultimately rejects or is already rejected. This promise is added to the chain and the promise chain takes on the rejected reason of the returned promise.
Throw an exception. If an exception is thrown inside the .then() handler, then the .then() infrastructure will catch it and turn the promise chain into a rejected state with the reject reason set to the value that is thrown. So, if you do throw new Error("User not found") inside a .then() handler, then that promise chain will be rejected with that error object as the reject reason.
In your specific code:
const doAsyncOp = () =>
$http.get(uri)
.then(
value => value,
reject => //...
);
There is no reason for the value => value. value is already the resolved value of the promise, you do not need to set it again.
And since the fat arrow function automatically returns any single statement, you're already returning the promise from $http.get().then() in your doAsyncOp() function. So, you can just do:
const doAsyncOp = () => $http.get(uri);
doAsyncOp().then(result => {
// handle the result here
}).catch(err => {
// handle error here
});
To get this to the client code, just return the promise from your function:
const doAsyncOp = () => $http.get(uri)
then in your client you can use:
doAsyncOp()
.then(value => {
// use value
}
.catch(err => { /* error */ }
I think you can use async and await for the resolved values.
function delay(t, v) {
return new Promise(resolve => {
setTimeout(resolve.bind(null, v), t);
});
}
async function(){
await myData = delay(1000, 1000);
console.log(myData);
}

Flattening promise chain with readable function name

I saw promise implementation in Handling multiple catches in promise chain which produce a very readable chain
return validateInput
.then(checkLoginPermission)
.then(checkDisableUser)
.then(changePassword);
However, in order to do this each function needs to return a value instead of a Promise? Since Promise can resolves to either value or a Promise so this is not a problem. My goal is to turn every function to have readable clear logic as such.
The problem occurs when trying to unwind the nested promise function
return validateInput
.then(function(resultA) {
return checkLoginPermission
.then (function(resultB) {
// Do something with resultA
})
});
Imagine the original implementation involves accessing the value from previous promise. With nested promise, it is easily achievable. But with flatten chain, I would need to break up each function like this
function validateInput = function (resultA ) {
return Promise.resolve({resultA : resultA, resultB :
}
function checkLoginPermission = function (mix ) {
let resultA = mix.resultA;
let resultB = mix.resultB
//Do something with resultA
...
}
This is worse when the last function in the chain rely on something from the very beginning. That means the value have to be passed down from the beginning of the chain even if it was not used.
So am I accidentally stepping on some kind of anti-pattern that might affect performance? How else can I achieve good readability without all these hassles?
This is actually where async and await come in. It's good when you need results across multiple asynchronous calls/promises to be in scope. If you can use that, I'd say try it.
async function foo () {
const input = await validateInput()
const hasPermission = await checkLoginPermission(input)
const result = await checkDisableUser(hasPermission)
return await changePassword(result)
}
Just pass the variables into what function as they need to be. Just showing an example there. I was also a bit unsure of how you're setting validateInput, i think you need to put await infront of the function call itself.
If you cannot use async/await, I usually go with your 2nd code snippet, or define the higher scope variables ontop:
let resultA
return validateInput
.then(function(result) {
resultA = result
return checkLoginPermission
.then (function(resultB) {
// Do something with resultA
})
});
Promises are pattern, related to functional programming, there direct passing data from one function to other is a basic (it's called compose, here examples: http://scott.sauyet.com/Javascript/Talk/Compose/2013-05-22/). So it's not anti-pattern by no means.
I can't see any problem in such pattern. You can pass any data you want to next Promises and in nested Promises grab what they need. It's preety transparent and clear:
function validateInput() {
return Promise.resolve({resultA: 1});
}
function checkLoginPermission(result) {
return new Promise(function(resolve, reject) {
// ...
// code
// ...
result.resultB = 2;
return resolve(result);
});
}
function checkDisableUser(result) {
return new Promise(function(resolve, reject) {
// grab some data from previous function
let resultB = result.resultB;
// ...
// code
// ...
result.resultC = 3;
return resolve(result);
});
}
function changePassword(result) {
return new Promise(function(resolve, reject) {
// grab some data from previous functions
let resultB = result.resultB;
let resultC = result.resultC;
// ...
// code
// ...
result.resultD = resultB * resultC;
return resolve(result);
});
}
validateInput()
.then(checkLoginPermission)
.then(checkDisableUser)
.then(changePassword);
Also you can collect data in some variable, declared before Promises and so you will not have to pass result. But it will destroy functional nature of Promises.
The inner .then(/* ... */) callbacks can return either a primitive value or a Promise that resolves to some value. If it is another promise then the next .then won't start until the inner promise is resolved. Essentially, Promises always resolve to a non-promise type. If you resolve or return another Promise, it will be automatically unwrapped.
I would like to propose a solution using ramda.js#pipeP().
The good thing about this function is that it resolves promises sequentially.
We can rewrite your example using pipeP():
import pipeP from 'ramda/src/pipeP'
pipeP([
checkLoginPermission,
checkDisableUser,
changePassword
])(initialValue)
.then(responseChangePassword => { ... })
The results of a previous promise are passed to the following one.

Understanding the Promises/A+ specification

The Promises/A+ specification is one of the smallest specifications. Hence, implementing it is the best way to understand it. The following answer by Forbes Lindesay walks us through the process of implementing the Promises/A+ specification, Basic Javascript promise implementation attempt. However, when I tested it the results were not satisfactory:
✔ 109 tests passed
✘ 769 tests failed
Clearly, the Promises/A+ specification is not as easy to implement as it seems. How would you implement the specification and explain your code to a novice? Forbes Lindesay does an excellent job explaining his code but unfortunately his implementation is incorrect.
What is a promise?
A promise is a thenable whose behavior conforms to the Promises/A+ specification.
A thenable is any object or function that has a then method.
Here's what a promise looks like:
var promise = {
...
then: function (onFulfilled, onRejected) { ... },
...
};
This is the only thing we know about a promise from the outset (excluding its behavior).
Understanding the Promises/A+ specification
The Promises/A+ specification is divided into 3 main parts:
Promise states
The then Method
The Promise Resolution Procedure
The specification does not mention how to create, fulfill or reject promises.
Hence, we'll start by creating those functions:
function deferred() { ... } // returns an object { promise, resolve, reject }
function fulfill(promise, value) { ... } // fulfills promise with value
function reject(promise, reason) { ... } // rejects promise with reason
Although there's no standard way of creating a promise yet the tests require us to expose a deferred function anyway. Hence, we'll only use deferred to create new promises:
deferred(): creates an object consisting of { promise, resolve, reject }:
promise is a promise that is currently in the pending state.
resolve(value) resolves the promise with value.
reject(reason) moves the promise from the pending state to the rejected state, with rejection reason reason.
Here's a partial implementation of the deferred function:
function deferred() {
var call = true;
var promise = {
then: undefined,
...
};
return {
promise: promise,
resolve: function (value) {
if (call) {
call = false;
resolve(promise, value);
}
},
reject: function (reason) {
if (call) {
call = false;
reject(promise, reason);
}
}
};
}
N.B.
The promise object only has a then property which is currently undefined. We still need to decide on what the then function should be and what other properties a promise object should have (i.e. the shape of a promise object). This decision will also affect the implementation of the fulfill and reject functions.
The resolve(promise, value) and reject(promise, value) functions should only be callable once and if we call one then we shouldn't be able to call the other. Hence, we wrap them in a closure and ensure that they are only called once between both of them.
We introduced a new function in the definition of deferred, the promise resolution procedure resolve(promise, value). The specification denotes this function as [[Resolve]](promise, x). The implementation of this function is entirely dictated by the specification. Hence, we'll implement it next.
function resolve(promise, x) {
// 2.3.1. If promise and x refer to the same object,
// reject promise with a TypeError as the reason.
if (x === promise) return reject(promise, new TypeError("Self resolve"));
// 2.3.4. If x is not an object or function, fulfill promise with x.
var type = typeof x;
if (type !== "object" && type !== "function" || x === null)
return fulfill(promise, x);
// 2.3.3.1. Let then be x.then.
// 2.3.3.2. If retrieving the property x.then results in a thrown exception e,
// reject promise with e as the reason.
try {
var then = x.then;
} catch (e) {
return reject(promise, e);
}
// 2.3.3.4. If then is not a function, fulfill promise with x.
if (typeof then !== "function") return fulfill(promise, x);
// 2.3.3.3. If then is a function, call it with x as this, first argument
// resolvePromise, and second argument rejectPromise, where:
// 2.3.3.3.1. If/when resolvePromise is called with a value y,
// run [[Resolve]](promise, y).
// 2.3.3.3.2. If/when rejectPromise is called with a reason r,
// reject promise with r.
// 2.3.3.3.3. If both resolvePromise and rejectPromise are called,
// or multiple calls to the same argument are made,
// the first call takes precedence, and any further calls are ignored.
// 2.3.3.3.4. If calling then throws an exception e,
// 2.3.3.3.4.1. If resolvePromise or rejectPromise have been called, ignore it.
// 2.3.3.3.4.2. Otherwise, reject promise with e as the reason.
promise = deferred(promise);
try {
then.call(x, promise.resolve, promise.reject);
} catch (e) {
promise.reject(e);
}
}
N.B.
We omitted section 2.3.2 because it's an optimization that depends upon the shape of a promise object. We'll revisit this section towards the end.
As seen above, the description of section 2.3.3.3 is much longer than the actual code. This is because of the clever hack promise = deferred(promise) which allows us to reuse the logic of the deferred function. This ensures that promise.resolve and promise.reject are only callable once between both of them. We only need to make a small change to the deferred function to make this hack work.
function deferred(promise) {
var call = true;
promise = promise || {
then: undefined,
...
};
return /* the same object as before */
}
Promise states and the then method
We've delayed the problem of deciding the shape of a promise object for so long but we can't delay any further because the implementations of both the fulfill and reject functions depend upon it. It's time to read what the specification has to say about promise states:
A promise must be in one of three states: pending, fulfilled, or rejected.
When pending, a promise:
may transition to either the fulfilled or rejected state.
When fulfilled, a promise:
must not transition to any other state.
must have a value, which must not change.
When rejected, a promise:
must not transition to any other state.
must have a reason, which must not change.
Here, “must not change” means immutable identity (i.e. ===), but does not imply deep immutability.
How do we know which state the promise is currently in? We could do something like this:
var PENDING = 0;
var FULFILLED = 1;
var REJECTED = 2;
var promise = {
then: function (onFulfilled, onRejected) { ... },
state: PENDING | FULFILLED | REJECTED, // vertical bar is not bitwise or
...
};
However, there's a better alternative. Since the state of a promise is only observable through it's then method (i.e. depending upon the state of the promise the then method behaves differently), we can create three specialized then functions corresponding to the three states:
var promise = {
then: pending | fulfilled | rejected,
...
};
function pending(onFulfilled, onRejected) { ... }
function fulfilled(onFulfilled, onRejected) { ... }
function rejected(onFulfilled, onRejected) { ... }
In addition, we need one more property to hold the data of the promise. When the promise is pending the data is a queue of onFulfilled and onRejected callbacks. When the promise is fulfilled the data is the value of the promise. When the promise is rejected the data is the reason of the promise.
When we create a new promise the initial state is pending and the initial data is an empty queue. Hence, we can complete the implementation of the deferred function as follows:
function deferred(promise) {
var call = true;
promise = promise || {
then: pending,
data: []
};
return /* the same object as before */
}
In addition, now that we know the shape of a promise object we can finally implement the fulfill and reject functions:
function fulfill(promise, value) {
setTimeout(send, 0, promise.data, "onFulfilled", value);
promise.then = fulfilled;
promise.data = value;
}
function reject(promise, reason) {
setTimeout(send, 0, promise.data, "onRejected", reason);
promise.then = rejected;
promise.data = reason;
}
function send(queue, callback, data) {
for (var item of queue) item[callback](data);
}
We need to use setTimeout because according to section 2.2.4 of the specification onFulfilled or onRejected must not be called until the execution context stack contains only platform code.
Next, we need to implement the pending, fulfilled and rejected functions. We'll start with the pending function which pushes the onFulfilled and onRejected callbacks to the queue and returns a new promise:
function pending(onFulfilled, onRejected) {
var future = deferred();
this.data.push({
onFulfilled: typeof onFulfilled === "function" ?
compose(future, onFulfilled) : future.resolve,
onRejected: typeof onRejected === "function" ?
compose(future, onRejected) : future.reject
});
return future.promise;
}
function compose(future, fun) {
return function (data) {
try {
future.resolve(fun(data));
} catch (reason) {
future.reject(reason);
}
};
}
We need to test whether onFulfilled and onRejected are functions because according to section 2.2.1 of the specification they are optional arguments. If onFulfilled and onRejected are provided then they are composed with the deferred value as per section 2.2.7.1 and section 2.2.7.2 of the specification. Otherwise, they are short-circuited as per section 2.2.7.3 and section 2.2.7.4 of the specification.
Finally, we implement the fulfilled and rejected functions as follows:
function fulfilled(onFulfilled, onRejected) {
return bind(this, onFulfilled);
}
function rejected(onFulfilled, onRejected) {
return bind(this, onRejected);
}
function bind(promise, fun) {
if (typeof fun !== "function") return promise;
var future = deferred();
setTimeout(compose(future, fun), 0, promise.data);
return future.promise;
}
Interestingly, promises are monads as can be seen in the aptly named bind function above. With this, our implementation of the Promises/A+ specification is now complete.
Optimizing resolve
Section 2.3.2 of the specification describes an optimization for the resolve(promise, x) function when x is determined to be a promise. Here's the optimized resolve function:
function resolve(promise, x) {
if (x === promise) return reject(promise, new TypeError("Self resolve"));
var type = typeof x;
if (type !== "object" && type !== "function" || x === null)
return fulfill(promise, x);
try {
var then = x.then;
} catch (e) {
return reject(promise, e);
}
if (typeof then !== "function") return fulfill(promise, x);
// 2.3.2.1. If x is pending, promise must remain pending until x is
// fulfilled or rejected.
if (then === pending) return void x.data.push({
onFulfilled: function (value) {
fulfill(promise, value);
},
onRejected: function (reason) {
reject(promise, reason);
}
});
// 2.3.2.2. If/when x is fulfilled, fulfill promise with the same value.
if (then === fulfilled) return fulfill(promise, x.data);
// 2.3.2.3. If/when x is rejected, reject promise with the same reason.
if (then === rejected) return reject(promise, x.data);
promise = deferred(promise);
try {
then.call(x, promise.resolve, promise.reject);
} catch (e) {
promise.reject(e);
}
}
Putting it all together
The code is available as a gist. You can simply download it and run the test suite:
$ npm install promises-aplus-tests -g
$ promises-aplus-tests promise.js
Needless to say, all the tests pass.

Node.js asynchronous control flow with 3 processes working on shared data

I have node.js app with 3 functions, each one has own interval (or can by fired by another event):
processOrders
clearExpiredOrders
clearCancelledOrders
Functions are working on shared data, so only one of them can access this data at the time.
When there is a collision, it should wait until the actual function ends and then run second function immediately, but only once.
For example, if there is processOrders running and clearCancelledOrders is triggered 100 times, it will run clearCancelledOrders only once after processOrders finishes its work.
How would you solve this in node.js? Or do you have another idea how to solve this task?
Here is an example of how to do this very simply with promises:
var p = Promise.resolve(); // assuming Bluebird promises or Node 0.11.13+ with Promises.
// create a new empty resolved promise.
function doFirst(act){
return p = p.then(function(){
return someFirstAction(act); // assumes someFirstAction returns a promise
})
}
function doSecond(act){
return p p.then(function(){
return someSecondAction(act); // assumes someFirstAction returns a promise
})
}
What this does is queue the operations on the single chain. When it resolves the chain resolves. It also returns the returned promise, so you can unwrap it and get the value.
For example:
doFirst(1);
doSecond(2);
// some point in the future
doFirst(3).then(function(value){
// both doFirst(1) and doSecond(2) are done here
// the value argument is the resolution value of doFirst(3)
});
If you're unsure on how to convert your API to promises - see this question.
Since, you also want to limit the number of times a particular action is run, you can create special methods for it:
doFirst.queued = false;
function doFirst(act){
if(doFirst.queued) return; // already queued
doFirst.queued = true;
return p = p.then(function(){
return someFirstAction(act).finally(function(){
doFirst.queued = false;
});
})
}
doSecond.queued = false;
function doSecond(act){
if(doSecond.queued) return; // already queued
doSecond.queued = true;
return p = p.then(function(){
return someSecondAction(act); // assumes someFirstAction returns a promise
}).finally(function(){
doSecond.queued = false;
});
}

Categories