This question already has an answer here:
How to chain and share prior results with Promises [duplicate]
(1 answer)
Closed 6 years ago.
Is there a better way to do this?
let foo;
return functionA().then(result => {
foo = result;
return functionB();
}).then(bar => {
return functionC(foo, bar);
});
Notice that the result of functionA is required input to functionC. Using a variable outside the promise scope works fine, but it feels kinda icky. Is there a clean idiomatic way to do this?
Please note that I do not have the opportunity to change the API of any of the functions I am calling.
You could try using Promise.all() which you can pass an array of promises and it provides an array of responses within the then() callback when all promises passed in have resolved. You can access those array values to pass into functionC:
Promise.all([functionA, functionB]).then(values => functionC(values[0], values[1]));
Might be a little cleaner (without nesting) as it doesn't look like the response from functionA needs to be passed into functionB.
Otherwise, nesting would look like:
return functionA().then(foo => {
return functionB().then(bar => {
return functionC(foo, bar);
});
});
One option is, as Alexander Staroselsky writes, to use Promise.all(functionA(), functionB()). This runs the two functions simultaneously. If that's what you want to happen, you can use that answer. If, however, you want them to happen one after the other and then also be able to pass the result onto another handler, you can do this:
function functionA() {
return new Promise(resolve => resolve(1));
}
function functionB() {
return new Promise(resolve => resolve(2));
}
function functionC(first, second) {
return first + second;
}
functionA()
.then(result => Promise.all([result, functionB()]))
.then(function([first, second]) {
return functionC(first, second);
})
.then(result => {
console.log(result);
});
The functions are obviously simplified -- but the lovely thing about Promises is that that doesn't matter: we don't care how complex they are or how long they take. Yay Promises!
The clever thing is that Promise.all doesn't mind if the values you pass are not Promises. If they are any other value, they are treated as a Promise that is resolved immediately. (In the same way that you can do Promise.resolve(42).then(...).) So we can do Promise.all([result, functionB()]). This says "give me a Promise that is resolved when you have a final value for both result and functionB() and pass both values on". That is immediately in the case of result and at some unspecified time in the case of functionB.
The returned values are then passed as an array to the next then function.
.then(function([first, second]) {
return functionC(first, second);
})
This then receives the values as an array (see the use of destructuring in the parameter list) and sends those values on to functionC. We then do one last then function to show the result.
Related
This question already has answers here:
How to use Array.prototype.filter with async?
(9 answers)
Closed 14 days ago.
I have 6 objects in array, so I need to filter them. The problem is that in filter am getting false on every 6 object, in that case I must get empty array, but am not getting empty array. It does not filter, don't know why ?...
const result = await Promise.all(
pricesForEachProductTogether.filter(async (finalResult) => {
const pkgHaveMultiDay: boolean = await filterFinalResultByTravelMultiDay(
finalResult,
multiDay
)
if (pkgHaveMultiDay) {
return true
}
return false
})
)
return result
This is my filter code. So in this case I am getting 6 false in pkgHaveMultiDay, so result must be empty array. But I am getting my objects... It does not filter. Why?
By having it as an async function, you will always return a Promise because that's how async functions work. The nature of Array.prototype.filter being synchronous means that it will not try to await promises for you, it will just copy truthy values and ignore falsy values. Promises are always truthy, so even if it's Promise<false>, it will act like true when converting the Promise itself to a boolean.
To fix this, make a separate array of the booleans to keep, using Promise.all to await all of the internal Promises, then filter using that boolean array.
const shouldKeepResult = await Promise.all(pricesForEachProductTogether.map(async(finalResult) => {
const pkgHaveMultiDay = await filterFinalResultByTravelMultiDay(
finalResult,
multiDay
);
return pkgHaveMultiDay;
}));
const result = pricesForEachProductTogether.filter((_, i) => shouldKeepResult[i]);
return result;
it works exactly as it is described at https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/filter
The moment with your code is that filter doesn't support async functions, so when you put async keyword - you literally make your function return a Promise (resolved with a value you return), which is considered as true (as any other object)
This question already has answers here:
Curly Brackets in Arrow Functions
(3 answers)
Arrow function without curly braces
(9 answers)
Closed 1 year ago.
I am a novice in javascript and I've been learning about promises, recently I started working in some simple list that is retrieved with a random delay (setTimeout), a classical I think. After trying and doing some research I found I post that helped me a lot, and it works fine, but after reviewing what that post suggest against I had done before I noticed that the difference are two brackets...I've been reading and trying to understand why that brackets make such a difference (both options are functional) but I don't get it.
Could some one explain what's the difference? The brackets I mentioned are in function loadIng(), lines 5 and 12, the open bracket in first line and the closing one in the second.
{
new Promise(resolve => {
...
})
}
If I run the code with the brackets it runs synchronously, but if I run it without the brackets it runs asynchronously...I really don't get it.
function loadIngs() {
p = Promise.resolve();
for (let [k, v] of someArray) {
p = p.then(() =>
{new Promise(resolve => { //this
delay = Math.random() * 2000;
console.log(`${k}:${v} comes delayed for ${delay}`)
setTimeout(() => {
console.log("executes some function")
resolve();
}, delay);
})} //this
);
}
}
The difference here has to do with the automatic return value of an arrow function that does not use brackets around a single statement function body.
If you're talking about the new Promise() in the loadIngs() function, here:
function loadIngs() {
p = Promise.resolve();
for (let [k, v] of ings) {
p = p.then(() =>
{new Promise(resolve => {
delay = Math.random() * 2000;
console.log(`${k} comes delayed for ${delay}`)
setTimeout(() => {
setIng(k, v);
resolve();
}, delay);
})}
);
}
}
then the brackets around that new Promise() there are somewhat optional.
A single statement arrow function body may either be declared with or without brackets. Here's a simple example:
setTimeout(() => console.log("hi"), 100)
setTimeout(() => { console.log("hi") }, 100)
These are nearly the same thing. Since the function body is a single statement console.log("hi"), the arrow function body may stand alone or may be surrounded with brackets. Either is fine and generates the same execute EXCEPT for the return value. If you use brackets, then you need a return statement if there's a specific return value you want. Without the brackets the value of the single statement automatically becomes your return value of that arrow function.
This return value makes a difference in your promise example. So, for the p.then(() => ...) structure in the loadIngs() function. It can either be:
p.then(() => { return new Promise(...) })
or it can be:
p.then(() => new Promise(...) )
These two execute identically. But, notice that this is NOT exactly the same as shown in your code. I added a return in front of the new Promise(). When you remove the return, then this:
p.then(() => { new Promise(...) })
Has no return value from the arrow function. That means that while you create a new promise, you don't return it from the .then() handler and thus it is not linked into the parent promise chain and you get multiple independent promise chains.
This WILL create an execution difference and is probably responsible for what you say when you say one "runs asynchronously". That isn't exactly how I would describe it, but it does create a difference in the timing of when things run because in one case you have a single linked promise chain and in the other case you have two separate, independent promise chains.
I'm trying to make some checks before saving an array of objects (objects[]) to the DB (mongoDB using mongoose):
Those objects are already sorted by date, so objects[0].date is lower than objects[1].date.
Each object should check that last related saved object has a different value (to avoid saving the same info two times). This means that I've to query to the DB before each save, to make that check, AND each of these object MUST be stored in order to be able to make the check with the right object. If objects are not stored in order, the last related saved object might not be the correct one.
In-depth explanation:
HTTP request is send to an API. It returns an array of objects (sortered by date) that I want to process and save on my Mongo DB (using mongoose). I've to iterate through all these objects and, for each:
Look for the previous related object stored on DB (which COULD BE one of that array).
Check some values between the 'already stored' and the object to save to evaluate if new object must be saved or could be discarded.
Save it or discard it, and then jump to next iteration.
It's important to wait each iteration to finish because:
Items on array MUST be stored in DB in order: first those which lower date, because each could be modified by some object stored later with a higher date.
If next iteration starts before previous has finished, the query that searchs for the previous object could not find it if it hasn't been stored yet
Already tried:
Using promises or async/await on forEach/for loops only makes that iteration async, but it keeps launching all iterations at once.
I've tried using async/await functions inside forEach/for loops, even creating my own asyncForEach function as shown below, but none of this has worked:
Array.prototype.asyncForEach = function(fn) {
return this.reduce(
(promise, n) => promise.then(() => fn(n)),
Promise.resolve()
);
};
Test function:
let testArray = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
testArray.asyncForEach(function(element) {
setTimeout(() => {
console.log(element);
}, Math.random() * 500);
});
Provided example should show numbers on order in every case. It's not a problem if internal function (setTimeout in the example) should return a promise.
What I think I need is a loop that waits some function/promise between iterations, and only starts the next iteration when the first is already finished.
How could I do that? Thanks you in advance!
const myArray = ['a','b','c','d'];
async function wait(ms) { // comment 3
return new Promise(resolve => setTimeout(resolve, ms));
}
async function doSomething() {
await myArray.reduce(async (promise, item) => {
await promise; // comment 2
await wait(1000);
// here we could await something else that is async like DB call
document.getElementById('results').append(`${item} `);
}, Promise.resolve()); // comment 1
}
setTimeout(() => doSomething(), 1000);
<div id="results">Starting in 1 second <br/></div>
You can also use reduce and async await which you already said you've tried.
Basically, if you read how reduce works you can see that it accepts 2 parameters, first being callback to execute over each step and second optional initial value.
In the callback we have first argument being an accumulator which means that it accepts whatever the previous step returns or the optional initial value for first step.
1) You are giving initial value of promise resolve so that you start your first step.
2) Because of this await promise you will never go into next step until previous one has finished, since that is the accumulator value from previous step, which is promise since we said that callback is async. We are not resolving promise per say here, but as soon as the previous step is finish, we are going to implicitly resolve it and go to next step.
3) You can put for example await wait(30) to be sure that you are throttling the Ajax requests and not sending to many requests to 3rd party API's, since then there is no way that you will send more than 1000/30 requests per second, even if your code executes really fast on your machine.
Hm, ok i am not 100% sure if i understand your question in the right way. But if you try to perform an async array operation that awaits for your logic for each item, you can do it like follow:
async loadAllUsers() {
const test = [1,2,3,4];
const users = [];
for (const index in test) {
// make some magic or transform data or something else
await users.push(test[index])
}
return users;
}
Then you can simply invoke this function with "await". I hope that helps you.
In asyncForEach function you are resolving a Promise, setTimeout doesn't return a Promise.So if you convert your setTimeout to Promise. It will work as expected.
Here is the modified code:
testArray.asyncForEach(function(element) {
return new Promise((resolve, reject) => {
setTimeout(() => {
console.log(element);
return resolve(element)
}, Math.random() * 500);
})
});
I'm currently studying Electron in Action. I'm learning JavaScript on-the-go and this didn't come up in google so I'd thought I'd ask it in here. Imagine we have this code:
newLinkForm.addEventListener('submit', (event) => {
event.preventDefault();
const url = newLinkUrl.value;
fetch(url)
.then(response => response.text())
.then(parseResponse)
.then(findTitle)
.then(title => storeLink(title, url))
.then(clearForm);
});
in the first, and the fourth ring of the chain, we've gave a name to the return value of the zeroth, and the third function. But what if there are more than one return values? Do we create a list? Can we call two functions in a promise chain as in:
then(returnvalue1=>funct1, returnvalue2=>funct2)
Can we do that? Thanks for the response.
A promise only has a single resolved value, so a .then() handler is only ever passed a single argument.
If you want to resolve a promise with multiple values, then you would typically wrap them in an array or an object and the single resolved value would be the array or the object.
You could use destructuring to then easily reference the multiple values wrapped in the object or array.
Example:
Promise.resolve([1,2]).then(result => {
console.log(result); // logs [1,2]
return result; // pass the array on to the next step
}).then(([a, b]) => { // use destructuring to get the two items out of the array
console.log(a);
console.log(b);
});
What you proposed like this:
.then(returnvalue1=>funct1, returnvalue2=>funct2)
is something entirely different. Here you're passing two functions to a .then() as in .then(f1, f2) (or that looks like what you're trying to do). When you pass a second function to .then() that second function is a reject handler (like a .catch() handler) and it's only called if the promise rejects and the argument would be the rejection reason.
Second then argument is reserved for error handler, then(returnvalue1=>funct1, returnvalue2=>funct2) isn't a correct way ti handle return values.
then callback receives the only one return value as a parameter, a value that previous then callback returns.
In case values from different then need to be used together, they should be either passed through entire promise chain as array or object value and destructured:
promise
.then(foo => {
const bar = 'bar';
return [foo, bar];
})
.then(([foo, bar]) => {
// can access both foo and bar
})
Or then should be nested to have access to the scope where necessary value is available:
promise
.then(foo => {
const bar = 'bar';
return Promise.resolve(bar);
.then(bar => {
// can access both foo and bar
})
})
This is one of the problems that async..await solves.
Besides lazy execution, are Tasks and Promises pretty much the same thing?
When I refer to a task, I refer to a class that is in its most basic behavior like the following:
class Task {
constructor(then) {
this.then = then;
}
map(mapper) {
return new Task((resolve, reject) => this.then(
x => resolve(mapper(x)),
reject
))
}
flatMap(mapper) {
return new Task((resolve, reject) => this.then(
x => mapper(x).then(resolve, reject),
reject
))
}
}
What type of (class?) is a task/promise? I'm learning about functional programming approaches, but I don't think I've gotten to this type yet. Is it a type of monad?
Yes, the key thing is a monadic bind, or a flatMap in your case that has the signature:
Task A -> A -> Task B -> Task B
With promises - that's the then method that:
Promise A -> (A -> Promise B) -> Promise B
this onFulfilled return value
In fact, both are instances of the Continuation Monad. Lots of other things (like Rx streams) are instances of the continuation monad.
Promises in JavaScript however are specced with a slightly different (and uglier, for arguably practical reasons) signature with it being possible to also return a plain value from then, and there are exception semantics involved.
There was a push for more "monadic" promises back in 2013 when they were specced, but it failed. The current promises in JavaScript aren't really "monad"s per-se.
A.promises are one refactoring step further than callback.just that.
if you have a function
const f = x => x * x ;
1) Use Callback and remove return type
you can pass a callback and remove the return type. This the famous Continuation-passing style
const f1 = (x,callback)=>callback(x*x);
What if we curry the action and return it as a result ?! we could do this :
const squareCont = (x)=>callback=>callback(x*x);
if you look at this for a long time and rename callback to resolve you will see that this is actually a promise.
//i rewrited it to make it clearer
var squareCont = function(x){
return function (resolve){return resolve (x*x);}
}
[Side Note : we could make it into a Promise if we enclose the callback into an object that exposes a then function like that :
const squarePromise = (x)=>({then:callback=>callback(x*x)});
squarePromise(2).then(r=>console.log(r))
check the fiddle here]
B.Task is the Coninuation Co-Monad . Because i cannot summarize it here you can read in more detail here : Promises & Continuation Monad in JavaScript
and here
Async/await a.k.a Continuation Co- Monad in JavaScript