JavaScript Promises: conventional way to pass arguments - javascript

How do i pass additional arguments to next "step" of promise?
new Promise((resolve, reject) => {
const a = // do stuff and return a string
return Promise.all([
// execute another promise,
// execute yet another promise
])
})
.then(([resultFromPromise_1, resultFromPromise_2]) => {
// how do i pass `const a` here?
})
I can add something like new Promise(resolve => resolve(a)) into Promise.all array, but this looks ugly. Is there better way to pass data in such cases?

I can add something like new Promise(resolve => resolve(a)) into Promise.all array, but this looks ugly. Is there better way to pass data in such cases?
Yes: Use then. If you already have a promise, using new Promise is never needed. then creates a promise, which waits for the resolution of the one you called it on, and then gets resolved with what you return from the then callback or gets rejected if you throw an exception. One of the keys of promises is how using then (and catch) transforms things at each link in the chain.
In that specific case, you'd use then on the original promise and use its callback to transform the result using a (although if you want to wait until they're all done, you can do that too; covered later).
Side note: The new Promise line at the beginning of the code of your question shouldn't be there, you don't return a promise out of the promise executor (the callback you pass to new Promise).
Example:
const a = "some string";
Promise.all([
getPromise("one").then(result => result + " - " + a), // ***
getPromise("two")
])
.then(results => {
console.log(results);
});
function getPromise(str) {
// (Could use Promise.resolve here; emphasizing asynchronousness)
return new Promise(resolve => {
setTimeout(() => {
resolve(str);
}, 250);
});
}
Alternately, if you really only want to use a when all of the promises you're passing to Promise.all have resolved, you can do that, too:
const a = "some string";
Promise.all([
getPromise("one"),
getPromise("two")
])
.then(([result1, result2]) => {
return [result1 + " - " + a, result2]; // ***
})
.then(results => {
console.log(results);
});
function getPromise(str) {
// (Could use Promise.resolve here; emphasizing asynchronousness)
return new Promise(resolve => {
setTimeout(() => {
resolve(str);
}, 250);
});
}

First off, your first promise has an error, you're not resolving it. You should do something like this:
new Promise((resolve, reject) => {
const a = 1;
resolve(Promise.all([
...
]))
})
And as for your question, instead of new Promise(resolve => resolve(a)) you can just pass a directly to the all array. ie:
new Promise((resolve, reject) => {
const a = 1;
resolve(Promise.all([
Promise.resolve("a"),
Promise.resolve("b"),
a,
]))
})
.then(([resultFromPromise_1, resultFromPromise_2, a]) => {
console.log(a);
})

Related

Can someone explain pls why the Promise returns pending in the first function, but resolved in the other two?

Can someone explain pls why the Promise returns pending in the first function, but resolved in the other two? When i read MDN it states that just using the word async wont make the code inside asynchronous (we need to use the word await as well). Also, I return the promise explicitly, since I use resolve() (which should return fulfilled promise as in two other functions, yet it returns Pending in the first function). I provided the code below.
const testFunc = async() => {
return new Promise((resolve,reject)=> {
resolve()
})
}
const testFunc2 = () => {
return new Promise((resolve,reject)=> {
resolve()
})
}
const testFunc3 = () => {
return new Promise((resolve,reject)=> {
resolve()
})
}
testFunc().then(()=> console.log("Hello")).then(()=> console.log("there")).then(()=> console.log("Obi One"))
testFunc2().then(()=> console.log(1)).then(()=> console.log(2)).then(()=> console.log(3))
testFunc3().then(()=> console.log(4)).then(()=> console.log(5)).then(()=> console.log(6))
console.log(testFunc())
console.log(testFunc2())
console.log(testFunc3())
// Results in the console:
// Promise {<pending>}
// Promise {<fulfilled>: undefined}
// Promise {<fulfilled>: undefined}
// 1
// 4
// 2
// 5
// Hello
// 3
// 6
// there
Thats because testFunc (the function with async keyword) is equalevent to this:
const testFunc = () => {
return new Promise((resolve,reject)=> {
resolve(new Promise((r) => {
r()
}))
})
}
and not
const testFunc = () => {
return new Promise((resolve,reject)=> {
resolve()
})
}
Using async queues the executer as a microtask.
Read about it here: https://whistlr.info/2021/async-and-tasks/
The key point:
When you write an asynchronous function, you force the caller to use a microtask to read its result. This is the case even if you don't await inside it—it will return a Promise regardless

What does Promise.all actually do under the hood?

I am trying to understand Promise.all here.
What I did here was to covert below code using Promise.all to achieve the same result.
I understand that Promise all combine the data1, data2.
My question here is that how does Promise.All work without resolve method?
Does Promise resolve those data within the method itself?
Please advise.
const readAllUsersChaining = () => {
return new Promise((resolve, reject) => {
let result = [];
getDataFromFilePromise(user1Path)
.then((data) => {
result.push(JSON.parse(data)); // what are you doing? he's gone mad...
return getDataFromFilePromise(user2Path);
})
.then((data) => {
result.push(JSON.parse(data));
result ? resolve(result) : reject(result);
});
});
};
const readAllUsers = () => {
const data1 = getDataFromFilePromise(user1Path);
const data2 = getDataFromFilePromise(user2Path);
console.log(data1, data2);
return Promise.all([data1, data2]).then((data) => {
return data.map((el) => JSON.parse(el));
});
};
My question here is that how does Promise.All work without resolve method?
Not quite sure what you mean. Promise.all simply creates a new promise internally that is resolved when all other promises are resolved.
Here is a simple implementation of Promise.all for the case that arguments are always promises:
function all(promises) {
if (promises.length === 0) {
return Promise.resolve([]);
}
return new Promise((resolve, reject) => {
const results = [];
let resolved = 0;
promises.forEach((promise, i) => {
promise.then(
result => {
results[i] = result;
resolved++;
if (resolved === promised.length) {
resolve(results);
}
},
error => reject(error)
);
});
}
Promise.all allows to wait until all promise passed as arguments to be fullfilled before the then method attach to be execute.
The real use case would be when you have to perform five call to an API, an you want to perform some treatement only when you have get the data from all the API call. you can rely on the Promise.all function which will wait all passed promised to be fullfilled for it to be fullfiled on it turn.
Bellow I provide an example of a Promise.all call. which has two Promises passed as argument. the first one has a timer which fullfilled it after 5 second and the second if fullfiled immediately but. the Promise.all will be fullfilled only when both of the promise passed as argument ar fullfilled
const firstPromise = new Promise((resolve, reject) => {
setTimeout(()=> {
return resolve({
name: "First Promise"
});
}, 5000);
});
const secondPromise = new Promise((resolve, reject) => {
return resolve({
name: "Second Promise"
});
})
const all = Promise.all([firstPromise, secondPromise]).then((response) => {
console.log(response);
});

Generate an array of promises to run sequentially

I'm trying to generate an array of Promises to run sequentially. I've seen lots of tips on this but can't get it to run in my use case.
export default function generateIcons(){
return new Promise((resolve, reject) => {
const containers = document.querySelectorAll('.html2CanvasTarget')
const promises = containers.map(child => processIcon(child))
promises.reduce((p, fn) => p.then(fn), Promise.resolve())
resolve()
})
}
function processIcon(child){
return new Promise((resolve, reject) => html2canvas(child).
then(canvas => uploadFromCanvas(canvas,
child.childNodes[0].className.split(' ')[1] + '.png'))
.then(resolve).catch(reject))
}
Any tips? This just rejects and I can't see why
Looks like you want to resolve the main promise when the canvases are available for all the child elements. You can use Promise.All() for this.
It should also be noted that the querySelectorAll doesn't return anything you can call the .map on. You will have to create an array from what the querySelectorAll returns - which is a NodeList.
export default function generateIcons(){
return new Promise((resolve, reject) => {
const containers = document.querySelectorAll('.html2CanvasTarget');
const promises = Array.from(containers).map(child => processIcon(child))
Promises.All(promises).then(() => resolve());
})
}
containers is a NodeList, and NodeLists don't have a .map method, which is why your code is throwing an error.
Because processIcon already returns a Promise, there's no need to use the Promise constructor again. html2canvas already returns a Promise too, so there's no need for any Promise constructor anywhere (see What is the explicit promise construction antipattern and how do I avoid it?)
If possible, just await each call of it in a for loop. Because uploadFromCanvas returns a Promise too, and you want to wait for it, return it (or await it) as well:
export default async function generateIcons() {
const containers = document.querySelectorAll('.html2CanvasTarget');
for (const container of containers) {
await processIcon(container);
}
}
function processIcon(child) {
return html2canvas(child, {backgroundColor:null})
.then(canvas => uploadFromCanvas(canvas, child.className.split(' ')[1] + '.png'))
.catch(console.log);
}
As per your question, you can use Q module module for that
You need to take an empty array and push promises into it, and just pass this array in Q method (Q.allSettled), Take a look with an example
const Q = require('q');
const promiseHolder = [];
for (let i = 0; i < 10; i += 1) {
promiseHolder.push('Your Promises');
}
Q.allSettled(promises)
.then((results) => {
results.forEach((result) => {
if (result.state === 'fulfilled') {
const value = result.value;
return value;
}
const reason = result.reason;
throw reason;
});
});
In Q.allSettled() The method you always get the result in .then(). There are 2 states. One for success and one for failure.
Success => state === 'fulfilled', value: 'Whatever your promise return'
Failure => state === 'rejected', reason: 'Whatever your promise thrown'
In this case, you have a number of successful and unsuccessful promises.
There is the second approach which is Promise.all() do the same but the issue is whenever any of promise rejected further promise never called.
const promiseHolder = [];
for (let i = 0; i < 10; i += 1) {
promiseHolder.push('Your Promises');
}
Promise.all(promiseHolder)
.then((results) => {
return results;
})
.catch((err) => {
throw err;
});
In the second approach ( Promise.all()), It consists of all your promises pushed from for loop. If any of promise rejected no more promise called and suddenly you got the state of promise rejection in Promise.all().
Promise.all(promiseHolder)
.then((results) => {
return results;
})
.catch((err) => {
console.log('Promise will reject here', err);
throw err;
});
I hope it helps, Happy Coding :)

Should I create a new Promise in each class method?

I would like to utilize the Promises in my class methods. In Promise antipatterns I read that creating a new promise for each new function is considered to be bad.
However, I don't want to return un-related promises in my project, so I thought of doing something like this:
class MyClass {
async getToken() {
return new Promise(
(resolve, reject) => {
// . . .
const token_str = '<response_from_http_request>';
resolve(token_str);
}
)
}
async doSomething(token) {
return new Promise(
(resolve, reject) => {
const result = // . . .
resolve(result);
}
)
}
async doAnotherSomething(token) {
return new Promise(
(resolve, reject) => {
const result = // . . .
resolve(result);
}
)
}
}
Then I would use it like this:
let instance = new MyClass();
(async () => {
const token = await instance.getToken();
const result1 = await instance.doSomething(token);
console.log(result1);
const result2 = await instance.doAnotherSomething(token);
console.log(result2);
})();
Does this seem like a valid way to do this, or is this an antipattern too? And if so, how can I avoid writing code like this?
EDIT: What if I need to make several sequential http calls, perform some actions on the results and then return a Promise based on it?
The way I understand, if I don't make a new Promise, I have to return the one made by the got.js library, which includes the http response data.
Instead, I want to return a Promise which contains the result of my class method.
Example:
async getCityWeather( city_name ) {
return new Promise(
(resolve, reject) => {
// get the city id based on its name
const city_id = await got(`https://my-api/getCityIdByName/${city_name}`);
// now get the weather info for the city with id `cityid`
const weather_info = await got(`https://my-api/weatherById/${city_id}`);
// make an object to return
const temperature = {
weather_info.temp_min,
weather_info.temp_max,
}
resolve(temperature);
// ... all error handling are omitted
}
)
}
I don't want to return a Promise that contains got.js return values, I want to return my values based on the http request calls.
async functions always return a Promise.
A function/method will return a Promise under the following circumstances:
You explicitly created and returned a Promise from it's body.
You returned a Promise that exists outside the method.
You marked it as async.
Since you can await a Promise and instance.doSomething is already an async-marked method, you can await it without needing to explicitly return a Promise.
Simply return it's result like you would in a regular synchronous method.
I don't want to return un-related promises in my project...
Unless you're actually doing something asynchronous in your method (accessing the file system, database calls, timers etc...), you don't need to wrap it in a Promise, nor await it when you need a result.
The most usual case where you actually need to wrap something in a Promise is if you have an asynchronous function that works using callbacks but you want to use it as a Promise.
// plain old callback-style asynchronous functions:
const getFooViaCallback = callback => {
setTimeout(() => {
callback('foo')
}, 150)
}
const getBarViaCallback = callback => {
setTimeout(() => {
callback('bar')
}, 150)
}
class Foo {
constructor() {}
getFooViaPromise() {
// wrap callback-style code in a Promise
// so we can await it.
return new Promise(resolve => {
getFooViaCallback(result => {
resolve(result)
})
})
}
getBarViaPromise() {
// wrap callback-style code in a Promise
// so we can await it.
return new Promise(resolve => {
getBarViaCallback(result => {
resolve(result)
})
})
}
getBaz() {
// no reason to wrap this in a Promise,
// since it's a synchronous method.
return 'baz'
}
async getFooBarBaz() {
const foo = await this.getFooViaPromise()
const bar = await this.getBarViaPromise()
const baz = this.getBaz()
return foo + ',' + bar + ',' + baz
}
}
;(async() => {
const foo = new Foo()
const result = await foo.getFooBarBaz()
console.log('foo.getFooBarBaz() result: ', result)
})()
I've ommited error handling in the above snippet for brevity but you should use throw in async-marked methods to raise errors. It's the equivalent of calling .reject() within a Promise.

Chained promises in Promise.all without wrapper Promise

Is it possible for Promise.all to return the last value of the chain without a wrapper promise?
Without using await, it doesn't work in my context
Without wrapper example :
function sum1(x){
return new Promise(resolve => {
setTimeout(t => resolve(x+1),3000)
})
}
const p1 = sum1(1);
p1
.then(sum1)
.then(sum1)
Promise.all([p1])
.then(v => console.log(v[0]));
It logs 2 instead of the expected 4.
But if I use a wrapper it works :
function sum1(x){
return new Promise(resolve => {
setTimeout(t => resolve(x+1),3000)
})
}
function sum3(x){
return sum1(x)
.then(sum1)
.then(sum1)
}
const p2 = sum3(1);
Promise.all([p2])
.then(v => console.log(v[0]));
But in my context it gets complicated if I need to create and name a wrapper function for every chain of promises...
Is this possible?
You can store the value returned by p1.then(sum1).then(sum1) and call Promise.all on this value. It waits for the resolution of the promise chain not only the first one. Here is an example:
function sum1(x) {
return new Promise(resolve => {
setTimeout(t => resolve(x + 1), 10);
});
}
const p1 = sum1(1);
const p2 = p1.then(sum1).then(sum1);
Promise.all([p1]).then(v => console.log('P1', v[0]));
Promise.all([p2]).then(v => console.log('P2', v[0]));
Actually all I had to do was call the chain in the variable declaration, so it references the last called promise
function sum1(x){
return new Promise(resolve => {
setTimeout(t => resolve(x+1),3000)
})
}
//The change is here
const p1 = sum1(1)
.then(sum1)
.then(sum1)
Promise.all([p1])
.then(v => console.log(v[0]));
Explanation: the problem with your code was that you store in const p1 = sum1(1); only first part of chain, and in Promise.all([p1]) you get result only from this first part (one solution is just to store all chain in p1 like this: p1=sum1(1).then(sum1).then(sum1). However in your case, you don't need to use Promie.all at all (because in your example there is only one promise p1/2 ):
function sum1(x){
return new Promise(resolve => {
setTimeout(t => resolve(x+1),300)
})
}
// Promise.all([sum1(1).then(sum1).then(sum1)]).then(r => console.log(r)); // this works too
sum1(1).then(sum1).then(sum1).then(r => console.log(r));
How about create a new function to do your task, it seems like promise.all does not fit to your case
const runInWaterfall = (promises) => new Promise((resolve, reject) => {
const result = promises.reduce((acc, curr, index) => {
if(!acc) {
return curr();
}
return acc.then(curr);
}, null);
result.then(resolve).catch(reject);
})
and your task can be rewritten as following
runInWaterfall([() => Promise.resolve(1), sum1, sum1, sum1]).then(result => console.log(result))

Categories