This question already has answers here:
How do I access previous promise results in a .then() chain?
(17 answers)
Closed 6 years ago.
There are many ways to make good control flows with ES2015 promises compared to alternatives like "callback hell" and similar. Lots of examples out there.
However, when the arguments for each step in the control flow depend on more than the return/resolved value of the previous step, it is harder to find a good syntax. Actual use cases are usually a bit more complex, so the simplified example syntaxes might not look that ugly, but it makes it easier to explain and discuss.
So, the question is how to make a simple (readable and easily maintainable) but flexible way of solving this kind of cases.
Example
Three operations in the control flow. Each being a function that returns a Promise.
function firstOperation(arg1) {
return new Promise(...);
}
function secondOperation(firstResponse) {
return new Promise(...);
}
function thirdOperation(firstResponse, secondResponse) {
return new Promise(...);
}
Simpler control flows
If each step just depended on the previous one, it could look simething like this:
firstOperation('foo')
.then(res1 => secondOperation(res1))
.then(res2 => thirdOperation(res2));
Or even simpler:
firstOperation('foo')
.then(secondOperation)
.then(thirdOperation);
No problem there. But in this case, thirdOperation require arguments from both of the first two operations.
The future
In ES8, I guess this could look something like:
const res1 = await firstOperation('foo');
const res2 = await secondOperation(res1);
const res3 = await thirdOperation(res1, res2);
The present
I want to use the completed standards, so I hope to find the best possible solution for these kind of cases with ES2015 syntax or possibly a promise/generator library.
Possible (but not very simple/readable) ways to solve it:
firstOperation('foo')
.then(res1 => secondOperation(res1)
.then(res2 => thirdOperation(res1, res2))
);
Making it a kind of "promise hell" that works, but in more complex cases (more than one level) would become ugly and hard to read/maintain.
Another solution:
firstOperation('foo')
.then(res1 => secondOperation(res1)
.then(res2 => ({ res1, res2 })))
.then(({ res1, res2 }) => thirdOperation(res1, res2));
Not much prettier, the only benifit compared to the previous one, is that if there where more than three operations, they could all be called on the same level, instead of indenting one level further each time. And after each operation, the result is being merged with the other ones into a "context object" keeping all the results.
Suggestions?
So, until a standardized way comes along (ES8 probably), I guess using some kind of promise/generator library would be an acceptable solution. What I mostly hope, is that I won't need to touch the functions themselves. Like still letting them (firstOperation, secondOperation and thirdOperation in the examples) get arguments the normal way, and return a promise that resolves with the value directly, instead of having to rewrite them to be able to fit different control flow use cases.
Suggestions for solving cases like this?
You could do something like this:
const res1 = firstOperation('foo');
const res2 = res1.then(secondOperation);
const res3 = Promise.all([res1, res2])
.then(args => thirdOperation(...args));
I usually declare variables before the promise chain and store the results there, where all of the calls can access them. It works, doesn't require increased indenting / deeper leveling, and it doesn't require you to change your function params.
let firstResponse;
firstOperation(userId)
.then(firstResp=>{
firstResponse = firstRestp;
return secondOperation(firstResponse);
})
.then(secondResponse=>{
return thirdOperation(firstResponse, secondResponse);
});
Edit
This answer in the linked duplicate question points out some drawbacks to this approach. For example, the variables might be used before being populated.
Related
I'm making a function which takes both .toArray() and without. Is there a conditional way to add this in es6, es7 or es8?
await db
.collection(collection)
.find(params)
.toArray() //I want tis conditionally added or not
In the sense of this (this doesn't work)
multiple && .toArray()
Or is this only possible on two methods?
Is there a way to shorten a member function of an object conditionally in JavaScript?
Not really. You have a chained set of methods and you can't insert something into the chain conditionally.
My sense is that the clearest option is this:
let x = db.collection(collection).find(params);
if (someCondition) x = await x.toArray();
There are other oddball things such as putting a no-op method on the object ahead of time and then executing a method from a variable that would sometimes contain "toArray" and sometimes contain "noop".
let x = await db.collection(collection).find(params)[someCondition ? "toArray" : "noop"]();
But, I don't think I'd ever write code this way myself as I don't see it as very clear.
FYI, your particular example is a little odd because .find() returns a cursor object and .toArray() returns a promise that resolves to an array. So, you're also asking to end up with different types of data one way vs. the other. It seems like this different result type is going to have to go down a different code path anyway so there's more to the conditional branch than just this one step. That indicates one should really look at the bigger picture for the overall problem than just this step or order to come up with the best solution.
I have an async function that looks something like this:
const myMethod = async () => {
await Promise.all(
someIds.map((id) => {
someMethodThatReturnsPromise(id);
});
);
doSomethingElseAfterPromisesResolve();
};
This function contains a bug because it uses curly braces in its map function but doesn't return each promise. The Promise.all consumes the undefined return value and silently proceeds to the next statement, without waiting. The problem can be corrected by using parentheses instead of braces, or by including an explicit return statement.
My question is, how can I test this? I know I can use Promise.resolve() or Promise.reject() to simulate different states of the promise, and mock the return values of the inner method, but that doesn't really cover the problem. Outside a full blown integration test, how can I prevent the above error with a test?
Well, the issue is not that Promise.all() accepts null, it doesnt. What it accepts is arrays of the kind of [null] or [undefined] (which are 2 different things, actually)
As I mentioned in my comments, testing Promise.all() is not something I would do, it's third party code, you should be testing your own, so I think having a linter check for such bugs is a far superior solution
That being said, you are entitled to your opinions, I'll merely point out a possibility for achieving what you want. That is: monkey patching
You could overwrite the Promise.all() like so:
let originalAll = Promise.all;
Promise.all = (promises) => {
// you could check several other things
// but this covers what you wanted, right?
let isArrayWithBlanks = promises.includes(null) || promises.includes(undefined);
return isArrayWithBlanks
? Promise.reject("NO!")
: originalAll(promises);
};
Now you can easily write a test given you use this monkey patched Promise.all throughout your project. You decide where to place that code
Hope this helps
I would stub both the someMethodThatReturnsPromise and doSomethingElseAfterPromisesResolve functions, returning any value from both.
Then ensure that someIds has multiple values.
Your assertions could then be:
someMethodThatReturnsPromise is called once for each item in the someIds array
doSomethingElseAfterPromisesResolve is called
This question already has answers here:
How do I return the response from an asynchronous call?
(41 answers)
Closed 5 years ago.
I'm currently discovering Google Datastore and it seems pretty useful.
However (I'm a JS newbie) I'm stuck with something pretty simple concerning Promise and Async/await, and I'm not able to find my answer (I tried...).
This get works perfectly into my terminal (it's fairly simple):
datastore.get(datastore.key(['viewing', 'abc123']))
.then((slot) => {
console.log(slot[0])
})
But what I want is to wrap this query into a const and return slot[0] on-demand...
So I've tried:
const wrap = () => {
datastore.get(datastore.key(['viewing', 'abc123']))
.then((slot) => {
return slot[0]
})
}
Didn't work.
I've tried to add a return before datastore.get, to change a return for a Promise.resolve... but it's still the same : Promise pending (best case).
I do not speak about using async/await.
I can't return my slot[0]...
Any clue, thanks.
What you are dealing with is a promise. The way I always think about it is once you enter "Promise-Land" you can't escape. Some may see that as a bad thing but i think it's great.
Once you run the get, at some point in the future it will finish. When it does it calls the function inside the then and passes it the value. From this point on you have to work inside of then statements. (caveat below)
You can keep a reference to the promise and use it as a value
const omg = datastore.get(datastore.key(['viewing', 'abc123']))
You can only get at the value by using the .then function.
omg.then(console.log)
You can take your value and pass it to another function, whether a lambda:
omg
.then(slot => slot[0])
.then(console.log)
or a named function
const head = list => list[0];
omg
.then(head)
.then(console.log)
Thats the best way to use promises
If that is too foreign or you aren't used to that type of programming (functional) then you can use the imperative Async/Await.
const omg = await datastore.get(datastore.key(['viewing', 'abc123']))
It should do what you expect but has to be transpiled by babel or similar.
This question already has answers here:
How to properly break out of a promise chain?
(3 answers)
Closed 6 years ago.
I have the situation of a chain of of ES6 thenables/promises. The first promise in the chain is really an asynchronous method that determines whether a cache may be refreshed in the first place, and the subsequent methods in the chain do the actual refreshing.
The thing is that if the first method determines that I should not actually refresh the cache, I want to bomb out of the chain.... I don't need to do the rest.
The only way I have been able to get this to work is to store away the result of the testing method and the on each subsequent .then() in the chain, to test whether to actually do something or to return a simple Promise.resolve(null).
This seems like a bunch of extra silly code that shouldn't really need to be there.
My code below works.... error handling is correct.... but is there a better way to code this kind of pattern, where you only want to conditionally continue with a chain? A way that doesn't involve storing away the answer and then forcing a Promise.resolve() on every then()?
function asyncRefreshCache(wDb, forceRefresh) {
var cachedDataVersions;
var allowRefresh;
return wDb.asyncCacheRefreshAllowed(forceRefresh) // Here I test whether I am allowed to really refresh the cache.
.then(function (ar) {
allowRefresh = ar;
return allowRefresh
? wDb.asyncGetCacheDataVersionsForRefresh() // Only do this if allowed above.
: Promise.resolve(null); //Yuck. Would love to just break out. But how?
})
.then(function (dataVersions) {
cachedDataVersions = dataVersions;
return allowRefresh
? asyncServerFlexSearch({ dataVersions: dataVersions, includeInactiveFlag: true, onLoadFlag: true }) // Only do this if allowed above.
: Promise.resolve(null); //Yuck. Would love to just break out. But how?
})
.then(function (serverResponse) {
return allowRefresh
? wDb.asyncMergeCleanDataFromServerToCache(cachedDataVersions, serverResponse) // Only do this if allowed above.
: Promise.resolve(null); //Yuck. Would love to just break out. But how?
});
}
EDIT: This question was marked as a duplicate because of similar questions about Q Promises and JQuery Promises. And JQuery Promises aren't evern really promises. So my initial hope was that the spec and implementation for ES6 Promises would accomodate this seemingly important need and use case. It appears that this may not be the case. So the answers to those other questions may be the "correct" answer to this question, but they are not about ES6 Promises. So that seems different to me.
but is there a better way to code this kind of pattern, where you only
want to conditionally continue with a chain?
You could throw an Error or use Promise.reject() from .then(), handle error and process error message or rejected promise reason at .catch()
I'm mostly using programming languages like Scala and JavaScript. I'm trying to understand the similarities and differences in how async reactive programming is used in both languages. Can you help me?
I'm not taking any particular Js Promise framework because it seems many implement the similar specifications (like Promise/A). I've only used Q so far.
It seems that in Javascript we call a Deferred the object we resolve to complete a Promise.
In Scala, it seems the Promise is the object you resolve to get a Future monad.
Can someone tell me if this is right? Is there any good reason for a different usage of the term Promise between Js and Scala?
Also, in Scala we usually chain Future monads with further computations using operators like map and flatMap (also called bindin Haskell). What is the equivalent of these in Js?
I may be wrong but it appears to me that in Js the then on a Promise kind of handle both map and flatMap operators right? If so, is it possible to obtain a promise of promise of result in Js? Like we can get a Future[Future[Result]] in Scala (which can be flattened to a Future[Result] anyway).
Is Js Promise a monad? It kind of seems so even if the method names do not really match those we find on monad literature.
Yes, and no.
While extremely similar. With JavaScript Promises that comply to the Promises/A+ spec .then is not really a monadic bind and does .map and .flatMap both. Inside a .then handler when you return a promise it will recursively unwrap it.
Promise.delay(1000).then(function() {
return Promise.delay(1000).then(function () {
return Promise.delay(2000);
}).then(function () {
return Promise.delay(5000)
});
}).then(function () {
alert("This is only shown after 8 seconds and not one");
});
(fiddle)
You are correct that the standard JS promise libraries and the A+ spec does not feature monadic promises. They have been discussed, and implementations like fantasy-promises exist. They follow a differnet spec and have little adoption. Also see this. There has been ongoing discussion about it in the language design discussion forum - esdiscuss and a monadic .chain method that does not flatmap and allows for monadic promises is considered but unlikely to make it.
This is for pragmatic reasons. The current way promises are implemented is immensely useful. Rare are the cases you actually want a Future[Future and normally you want continuations to just work in the language. Promises 'borrow' from monads and are 'monadic' in a sense themselves. .then is very close to bind and in my head I use them interchangeably :)
It is impossible to have a Promise[Promise[Value]] like a Future[Future[Value]] in Scala with most promise libraries. You'd have to wrap it in an object and have Promise[Container[Promise[Value]]].
Promise.delay(1000).then(function () {
return Promise.delay(1000).then(function () {
return {
wrap: Promise.delay(2000).then(function () {
return Promise.delay(5000);
})
};
});
}).then(function () {
alert("This logs after 1 second");
// I've also not seen a really solid use case
// except TypeScript type inference which is meh
});
(fiddle)
There are also a number of other smaller differences between the two, but generally you are correct in your assertions.
It seems that in Javascript we call a Deferred the object we resolve to >complete a Promise. In Scala, it seems the Promise is the object you >resolve to get a Future monad.
Can someone tell me if this is right? Is there any good reason for a >different usage of the term Promise between Js and Scala?
In Scala, Promise and Future have separated functionality, Future is a asynchronous computation container, which return you some value in the future, and Promise is the writing part for async-computation, which you can do something as follow
val promise = Promise[String]
val future1 = promise.future
val future2 = future1.map { case s => println(s); s }
future2.onSuccess { case s => println(s + " 2nd time") }
promise.success("promise completed")
Once you execute the last statement, the output will be
promise completed
promise completed 2nd time
In Scala,you read value from Future using onComplete, or you chain it using map, and you write to a Future using it's Promise counterpart
In JS Promise A+ specs, they are bundled together, Promise.then is used for both chaining and retrieving value for side-effect (eg. console.log), to write you will use resolve like code snippet below
var promise = new Promise(function(resolve, reject){
Thread.sleep(10000);
resolve("promise completed");
}
I'm trying to understand the similarities and differences in how async reactive programming is used in both languages.
This document here doesn't compare Javascript promises with Scala, but instead Javascript promises with C++ C# and Python: https://github.com/KjellSchubert/promise-future-task. I know thats not exactly what you had asked for, but this might give you some interesting pointers nonetheless.
In contrast to Scala,
the JS Promise is not a monad,
due to the implicit "thenable" unwrapping breaking monadic law.
You can, however, implement a callback-based monadic semantics and functionality, serving the same purpose.
See e.g. the cpsfy library.
In addition, there is a structural difference due to .then accepting 2 functions, while .chain accepts only one. However, a chain accepting 2 or even any number of argument functions can be implemented, like e.g. with
CPS wrapper from cpsfy:
//function returning CPS function with 2 callbacks
const readFileCps = file => (onRes, onErr) =>
require('fs').readFile(file, (err, content) => {
err ? onErr(err) : onRes(content)
})
// CPS wraps a CPS function to provide the API methods
const getLines = CPS(readFileCps('name.txt'))
// map applies function to the file content
.map(file => file.trim())
.filter(file => file.length > 0)
// chain applies function that returns CPS function
.chain(file => readFileCps(file))
.map(text => text.split('\n'))
// => CPS function with 2 callbacks
// To use, simply pass callbacks in the same order
getLines(
lines => console.log(lines), // onRes callback
err => console.error(err) // onErr callback
)