How to structure nested Promises - javascript

I have a situation where I think the only choice for me is to nest some Promises within each other. I have a Promise that needs to be performed and a method that does something until that Promise is complete. Something like this:
let promise = new Promise((resolve, reject) => {
// Do some stuff
});
doSomethingUntilPromiseisDone(promise);
However, within my Promise, I need to execute another method that returns another Promise:
let promise = new Promise((resolve, reject) => {
fetchValue(url)
.then((value) => {
// Do something here
}).catch((err) => {
console.error(err);
});
});
doSomethingUntilPromiseisDone(promise);
But now, in the fetchValue method's then statement, I have another method I need to execute that, guess what, returns another Promise:
let promise = new Promise((resolve, reject) => {
fetchValue(url)
.then((value) => {
saveToCache(value)
.then((success) => {
console.log('success!!');
resolve('success');
});
}).catch((err) => {
console.error(err);
});
});
doSomethingUntilPromiseisDone(promise);
So in the end, I have a Promise, within a Promise, within a Promise. Is there someway I can structure this better so that it is more straightforward? It seems like nesting them within each other is counter to Promise's intended chaining approach.

Use .then()
let doStuff = (resolve, reject) => {/* resolve() or reject() */};
let promise = new Promise(doStuff);
doSomethingUntilPromiseisDone(
promise
.then(value => fetchValue(url))
.then(value => value.blob())
.then(saveToCache)
)
.then(success => console.log("success!!"))
.catch(err => console.error(err))

you can use generator to flatten your nested promises (Bluebird.couroutine or Generators)
//Bluebird.couroutine
const generator = Promise.coroutine(function*() {
try {
const value = yield fetchValue(url);
const success = yield saveToCache(value);
console.log('success:', success);
} catch(e) {
console.error(err);
}
}));
generator();

Each function will call the next one with the result of the method before.
var promises = [1,2,3].map((guid)=>{
return (param)=> {
console.log("param", param);
var id = guid;
return new Promise(resolve => {
// resolve in a random amount of time
setTimeout(function () {
resolve(id);
}, (Math.random() * 1.5 | 0) * 1000);
});
}
}).reduce(function (acc, curr, index) {
return acc.then(function (res) {
return curr(res[index-1]).then(function (result) {
console.log("result", result);
res.push(result);
return res;
});
});
}, Promise.resolve([]));
promises.then(console.log);

Related

Taking a value from Promise and returning it instead of console.log

I have an exercise where I have to build some async functions (not using await/async, Promises are allowed).
funcs need to be a list of those x functions I make.
Each of the function has to receive callback as an argument.
The callback is made with (err,res) where err could be either null or the statement of error (I don't know how to determine any error there).
Function together need to return an Array with the results of those functions, for which the pred is fulfilled.
Before I go to together function, I have to ask you, how to get into the value of Promise if I don't want to console.log it, but I want for example to either return it in callback or just put it into a const. If I make res.then(...) I am only able to console.log that result, but am not able to put it into let result (because of async probably the result is always returned as 0).
My code:
function fun1(value,cb) {
const promise1 = new Promise((resolve, reject) => {
setTimeout(() => resolve( value * 100), 500)
})
return cb(null,promise1)
}
function fun2(value,cb) {
const promise2 = new Promise((resolve, reject) => {
setTimeout(() => resolve( value * 100), 500)
})
return cb(null, promise2)
}
function fun3(value,cb) {
const promise3 = new Promise((resolve, reject) => {
setTimeout(() => resolve( value * 100), 2000)
})
return cb(null, promise3)
}
function callback(err, res) {
let result = 0;
res.then(x => {
if(x>200) {
console.log(x)
result = x
}
else {
console.log(err)
result = err
}
})
}
console.log(fun1(1,callback))
//const together = (funcs, pred) => (n) => { ... };

How to implement Promise.race() with async/await

How can I implement Promise.race() method with async and await?
async function promiseRace(promises) {
const results = [];
for (const p of promises) {
await p;
results.push(p);
}
return results[0];
}
I've tried to implement it like above but this doesn't work.
You can't. When using await you halt execution onto one specific promise. To implement Promise.race manually, you have to fiddle with callbacks:
function race(promises) {
return new Promise((resolve, reject) => {
for(const promise of promises)
promise.then(resolve, reject);
});
}
You can't. Just like you cannot implement the Promise constructor using async/await. Remember that await is only syntactic sugar for then calls - and you cannot implement the basic promise combinators using only that.
You can by using a wrapper promise along with async await. In the wrapper promise, protect the resolve/reject so that only the first promise wins.
Promise.race example using async/await:
// Implements promise.race
const race = async (promises) => {
// Create a promise that resolves as soon as
// any of the promises passed in resolve or reject.
const raceResultPromise = new Promise((resolve, reject) => {
// Keep track of whether we've heard back from any promise yet.
let resolved = false;
// Protect the resolve call so that only the first
// promise can resolve the race.
const resolver = (promisedVal) => {
if (resolved) {
return;
}
resolved = true;
resolve(promisedVal);
};
// Protect the rejects too because they can end the race.
const rejector = (promisedErr) => {
if (resolved) {
return;
}
resolved = true;
reject(promisedErr);
};
// Place the promises in the race, each can
// call the resolver, but the resolver only
// allows the first to win.
promises.forEach(async (promise) => {
try {
const promisedVal = await promise;
resolver(promisedVal);
} catch (e) {
rejector(e);
}
});
});
return raceResultPromise;
};
// *************
// Test Methods
// *************
const fetch = async (millis) => {
await waitMillis(millis);
return 'Async result: ' + millis + ' millis.';
};
const waitMillis = (millis) => {
return new Promise((resolve) => {
setTimeout(() => {
resolve();
}, millis);
});
};
const run = async () => {
let result;
result = await race([fetch(1), fetch(2), fetch(3)]);
console.log('Winner', result);
result = await race([fetch(3), fetch(2), fetch(1)]);
console.log('Winner', result);
result = await race([fetch(10), fetch(3), fetch(4)]);
console.log('Winner', result);
};
run();
Why not:
const race = async (promiseArr) => {
return Promise.race(promiseArr)
}
And inside your async function:
let dayAtTheRace = await race([
my.promiseFunc(),
my.wait(10)
])
function promiseRace(promises) {
return new Promise((resolve, reject) => {
promises.forEach(async (promise) => {
try {
const result = await promise;
resolve(result);
} catch (err) {
reject(err);
}
});
});
Here is my solution:
async function promiseRace(promises) {
const results = [];
for (const p of promises) {
results.push(await p);
}
return results[0];
}

Why does async array map return promises, instead of values

See the code below
var arr = await [1,2,3,4,5].map(async (index) => {
return await new Promise((resolve, reject) => {
setTimeout(() => {
resolve(index);
console.log(index);
}, 1000);
});
});
console.log(arr); // <-- [Promise, Promise, Promise ....]
// i would expect it to return [1,2,3,4,5]
Quick edit:
The accepted answer is correct, by saying that map doesnt do anything special to async functions. I dont know why i assumed it recognizes async fn and knows to await the response.
I was expecting something like this, perhaps.
Array.prototype.mapAsync = async function(callback) {
arr = [];
for (var i = 0; i < this.length; i++)
arr.push(await callback(this[i], i, this));
return arr;
};
var arr = await [1,2,3,4,5].mapAsync(async (index) => {
return await new Promise((resolve, reject) => {
setTimeout(() => {
resolve(index);
console.log(index);
}, 1000);
});
});
// outputs 1, 2 ,3 ... with 1 second intervals,
// arr is [1,2,3,4,5] after 5 seconds.
Because an async function always returns a promise; and map has no concept of asynchronicity, and no special handling for promises.
But you can readily wait for the result with Promise.all:
try {
const results = await Promise.all(arr);
// Use `results`, which will be an array
} catch (e) {
// Handle error
}
Live Example:
var arr = [1,2,3,4,5].map(async (index) => {
return await new Promise((resolve, reject) => {
setTimeout(() => {
resolve(index);
console.log(index);
}, 1000);
});
});
(async() => {
try {
console.log(await Promise.all(arr));
// Use `results`, which will be an array
} catch (e) {
// Handle error
}
})();
.as-console-wrapper {
max-height: 100% !important;
}
or using Promise syntax
Promise.all(arr)
.then(results => {
// Use `results`, which will be an array
})
.catch(err => {
// Handle error
});
Live Example:
var arr = [1,2,3,4,5].map(async (index) => {
return await new Promise((resolve, reject) => {
setTimeout(() => {
resolve(index);
console.log(index);
}, 1000);
});
});
Promise.all(arr)
.then(results => {
console.log(results);
})
.catch(err => {
// Handle error
});
.as-console-wrapper {
max-height: 100% !important;
}
Side note: Since async functions always return promises, and the only thing you're awaiting in your function is a promise you create, it doesn't make sense to use an async function here anyway. Just return the promise you're creating:
var arr = [1,2,3,4,5].map((index) => {
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve(index);
console.log(index);
}, 1000);
});
});
Of course, if you're really doing something more interesting in there, with awaits on various things (rather than just on new Promise(...)), that's different. :-)
Since it is async, the values have not been determined at the time map returns. They won't exist until the arrow function has been run.
This is why Promises exist. They are a promise of a value being available in the future.

Promise return multiple values

I use the following code to return promise which is working OK.
The promise return the data value
run: () => {
return new Promise((resolve, reject) => {
....
}).then((data) => {
let loginApi = data[0]
let test = 1;
}).catch((err) => {
if (err.statusCode === 302) {
var data = url.parse(err.response.headers.location, true)
resolve(data )
}
})
});
I call it
module.run()
.then((data) => {
And I was able to get the data.
now I want to return also value test in the resolve, how should I do it?
I try to add it like this
resolve({data,test});
resolve([data,test]);
with call like
module.run()
.then({data,test}) => {
without success(test is empty), I read about spread but this is the only option?
I use ES6 with bluebird latest version
If you are using promise chain, in promise chain you have then->then->catch->... format. Always return Promise.resolve or Promise.reject. Promise.resolve will give success result for next then block and Promise.reject will go to next catch block.
var module = {
run: () => {
return new Promise((resolve, reject) => {
// ....
resolve('promise resolved')
}).then((data) => {
let loginApi = data[0]
let test = 1;
return Promise.resolve({data,test})
}).catch((err) => {
if (err.statusCode === 302) {
var data = url.parse(err.response.headers.location, true)
return Promise.resolve({data, test});
}
return Promise.reject(err);
})
}
};
module.run().then(({data, test}) => {
console.log(data, test);
})

How to .catch a Promise.reject

I have a helper function for using fetch with CouchDB which ends as:
...
return fetch(...)
.then(resp => resp.ok ? resp.json() : Promise.reject(resp))
.then(json => json.error ? Promise.reject(json) : json)
and when I use it elsewhere, I was under the impression that I could .catch those explicit rejections:
above_function(its_options)
.then(do_something)
.catch(err => do_something_with_the_json_error_rejection_or_resp_not_ok_rejection_or_the_above(err))
but alas, I can't seem to be able to get a hold of the rejections.
The specific error I'm after is a HTTP 401 response.
What gives?
(Please note that there are implicit ES6 return's in the .thens)
function test() {
return new Promise((resolve, reject) => {
return reject('rejected')
})
}
test().then(function() {
//here when you resolve
})
.catch(function(rej) {
//here when you reject the promise
console.log(rej);
});
Make sure every call to a then() returns a value.
For e.g.
var url = 'https://www.google.co.in';
var options = {};
var resolves = Promise.resolve();
resolves.then(() => {
console.log('Resolved first promise');
var fetchPromise = fetch(url, options);
fetchPromise.then(() => {
console.log('Completed fetch');
});
})
.catch(error => {
console.log('Error', error);
});
Notice the console shows an uncaught exception. However, if you returned the inner promise (or any other value, which ends up turning into a promise via resolve), you end up flattening the promise so exception bubble up.
var url = 'https://www.google.co.in';
var options = {};
var resolves = Promise.resolve();
resolves.then(() => {
console.log('Resolved first promise');
var fetchPromise = fetch(url, options);
return fetchPromise.then(() => {
console.log('Completed fetch');
});
})
.catch(error => {
console.log('Error', error);
});
Notice the exception bubbles up to the outer promise. Hope this clears up things a little bit.
Why not wrap it in a try / catch block
// define a failing promise
const test = ()=> new Promise((resolve, reject) => reject('rejected'));
// using an immediately executing function to call an async block
(async ()=> {
try {
await test(); // => this will throw an error
} catch (er) {
console.log(er); // 'rejected'
}
})();
Promise rejections fall to the second param of the then function.
function test() {
return new Promise((resolve, reject) => {
return reject('rejected')
})
}
test().then(function() {
//here when you resolve
}, function(rej) {
//here when you reject the promise
console.log(rej)
})

Categories