Execute function once two promises have been finalized - javascript

I need the function "finalizeProcess" to be executed once all the promises of the "processPhotos" function are finished.
Can anybody help me?
Thank!!
processPhotos();
finalizeProcess();
processPhotos (){
this.foto1File.generateBlob((blob) => {
ref.child('picture_1').put(blob)
.then((pictureRef) => {
//sentence
})
})
this.foto2File.generateBlob((blob) => {
ref.child('picture_2').put(blob)
.then((pictureRef) => {
//sentence
})
})
}

This will do the trick:
function processPhotos() {
var promise1 = new Promise(resolve => {
this.foto1File.generateBlob(blob => {
ref.child('picture_1').put(blob)
.then(pictureRef => {
resolve(pictureRef);
});
});
});
var promise2 = new Promise(resolve => {
this.foto2File.generateBlob(blob => {
ref.child('picture_2').put(blob)
.then(pictureRef => {
resolve(pictureRef);
});
});
});
Promise.all([promise1, promise2]).then(results => {
// do something with results here
finalizeProcess();
});
};

As to what trincot had said, you can use Promise.all
Store your functions into a variable and then resolve the promises together.
processPhoto(){
const firstPromise = this.fotoFile.generateBlob(blob => {
//[..]
}
const secondPromise = this.foto2File.generateBlob(blob => {
//[...]
}
const [foto1File, foto2File] = Promise.all([firstPromise, secondPromise]);
}
Of course in each function, make sure to be returning the promise itself.

Here another alternative, similar to the answer of davmich. But instead of calling finalizeProcess inside processPhotos a promise is returned, which you can use to call finalizeProcess.
function processPhotos() {
return Promise.all([
new Promise(resolve => {
this.foto1File.generateBlob(blob => {
ref.child('picture_1').put(blob).then(resolve);
});
}),
new Promise(resolve => {
this.foto2File.generateBlob(blob => {
ref.child('picture_2').put(blob).then(resolve);
});
})
]);
}
processPhotos().then(pictureRefs => finalizeProcess());
Alternatively you can unfold to do some processing first.
ref.child('picture_1').put(blob).then(resolve);
Becomes:
ref.child('picture_1').put(blob).then(pictureRef => {
// do stuff with pictureRef
resolve(/* custom value */);
});

Related

Proper use of async JS to ensure function calls wait for previous functions to complete (resolve?)

Trying to learn proper async/await JavaScript to run functions in sequence when an early function in the sequence would be delayed (using setTimeout to simulate). I'm not getting the expected results (still getting "first", "second", "this should run first?" - see code).
What am I missing? Do I have the wrong idea about this?
Thanks in advance!
const zeroFunction = () => {
setTimeout(() => {
return new Promise((resolve) => {
console.log("This should run first?");
resolve();
});
}, 2000)}
const firstFunction = () => {
return new Promise((resolve) => {
console.log("first");
resolve();
})
}
const secondFunction = () => {
return new Promise((resolve) => {
console.log("second");
resolve();
})
}
async function fnAsync() {
await zeroFunction();
await firstFunction();
secondFunction();
}
fnAsync();
zeroFunction is currently returning undefined implicitly, not a Promise. Inverse the wrapping of the setTimeout and Promise constructor and it should work as expected.
const zeroFunction = () => {
return new Promise((resolve) => {
setTimeout(() => {
console.log("This should run first?")
resolve()
}, 2000)
})
}
const firstFunction = () => {
return new Promise((resolve) => {
console.log("first")
resolve()
})
}
const secondFunction = () => {
return new Promise((resolve) => {
console.log("second")
resolve()
})
}
async function fnAsync() {
await zeroFunction()
await firstFunction()
secondFunction()
}
fnAsync()

How to create javascript promise chain with array function?

I am facing a weired issue when creating a js promise chain.In promise,when I am using array function with (),I don'nt get the expected value.It give me the 'undefined' value in second then.
Here is the js code:
let x = new Promise((resolve, reject) => {
setTimeout(() => {
resolve('sonet970#gmail.com');
}, 2000);
});
function y(email) {
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve(email);
}, 4000);
});
}
x.then((res) => {
y(res);
})
.then((res) => {
console.log(res);
})
.catch((err) => {
console.log(err);
});
But when I didn't use the ()=>{} syntax inside the .then,I got the expected answer.
Here is the example of wright code:
let x = new Promise((resolve, reject) => {
setTimeout(() => {
resolve('sonet970#gmail.com');
}, 2000);
});
function y(email) {
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve(email);
}, 4000);
});
}
x.then((res) => y(res))
.then((res) => console.log(res))
.catch((err) => console.log(err));
Can anyone please help me with this issue?
In order to chain promises you need to return Promise.
This sample works correctly
x.then((res) => y(res))
.then((res) => console.log(res))
.catch((err) => console.log(err));
because (res) => y(res) means:
(res) => {
return y(res)
}
and the result of y() promise is passed to the next .then
So to solve your code you need to write it in this way:
x.then((res) => {
// do some calculations
return y(res);
})
.then((res) => {
// result of y promise
console.log(res);
})
.catch((err) => {
console.log(err);
});
Returning something from a function using curly braces {} means that you need to use keyword return to return something:
x.then((res) => {
return y(res);
});
Using arrow functions, if no curly braces added, the immediately value after => is returned.
then((res) => console.log(res));
Thank you all for your answers.Now I understand,why my first code din't work.It all about array function,nothing w
ith promises!

confused with .then() in promises

I'm new to js and I'm learning promises. I came up with this code which will print the resolved values from every function and will call new functions using .then
function login() {
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve({username : 'default'})
}, 1000)
})
}
function getVideos() {
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve(['vid1', 'vid2'])
},1000)
})
}
function getDesc() {
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve('desc')
}, 1000)
})
}
const test = login()
test.then(res => {
console.log(res)
getVideos()
})
.then(res => {
console.log(res)
getDesc()
})
.then(res => console.log(res))
But, I'm not getting the expected result, I thought all the statements in .then() needed to be executed and resolved to continue to the next .then() statement. But clearly this is not the case here as I'm getting the following as the output -
{ username: 'default' }
undefined
undefined
But I expected the output to be similar to this -
{username : 'default}
['vid1', 'vid2']
desc
pls, point where I'm going wrong here. Any help is appreciated, thanks a lot
You need to add return to inside your chains when you're calling those functions. For example:
function login() {
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve({username : 'default'})
}, 1000)
})
}
function getVideos() {
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve(['vid1', 'vid2'])
},1000)
})
}
function getDesc() {
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve('desc')
}, 1000)
})
}
const test = login()
test.then(res => {
console.log(res)
return getVideos()
})
.then(res => {
console.log(res)
return getDesc()
})
.then(res => console.log(res))
This is a good guide: https://javascript.info/promise-chaining
Make a couple of very minor changes:
const test = login()
test.then(res => {
console.log(res)
return getVideos()
})
.then(res => {
console.log(res)
return getDesc()
})
.then(res => console.log(res))
To chain promises together, you need to return the promises at each step. As it is you're implicitly returning undefined which is not a promise.

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.

Is it safe to resolve a promise multiple times?

I have an i18n service in my application which contains the following code:
var i18nService = function() {
this.ensureLocaleIsLoaded = function() {
if( !this.existingPromise ) {
this.existingPromise = $q.defer();
var deferred = this.existingPromise;
var userLanguage = $( "body" ).data( "language" );
this.userLanguage = userLanguage;
console.log( "Loading locale '" + userLanguage + "' from server..." );
$http( { method:"get", url:"/i18n/" + userLanguage, cache:true } ).success( function( translations ) {
$rootScope.i18n = translations;
deferred.resolve( $rootScope.i18n );
} );
}
if( $rootScope.i18n ) {
this.existingPromise.resolve( $rootScope.i18n );
}
return this.existingPromise.promise;
};
The idea is that the user would call ensureLocaleIsLoaded and wait for the promise to be resolved. But given that the purpose of the function is to only ensure that the locale is loaded, it would be perfectly fine for the user to invoke it several times.
I'm currently just storing a single promise and resolve it if the user calls the function again after the locale has been successfully retrieved from the server.
From what I can tell, this is working as intended, but I'm wondering if this is a proper approach.
As I understand promises at present, this should be 100% fine. The only thing to understand is that once resolved (or rejected), that is it for a defered object - it is done.
If you call then(...) on its promise again, you immediately get the (first) resolved/rejected result.
Additional calls to resolve() will not have any effect.
Below is an executable snippet that covers those use cases:
var p = new Promise((resolve, reject) => {
resolve(1);
reject(2);
resolve(3);
});
p.then(x => console.log('resolved to ' + x))
.catch(x => console.log('never called ' + x));
p.then(x => console.log('one more ' + x));
p.then(x => console.log('two more ' + x));
p.then(x => console.log('three more ' + x));
I faced the same thing a while ago, indeed a promise can be only resolved once, another tries will do nothing (no error, no warning, no then invocation).
I decided to work it around like this:
getUsers(users => showThem(users));
getUsers(callback){
callback(getCachedUsers())
api.getUsers().then(users => callback(users))
}
just pass your function as a callback and invoke it as many times you wish! Hope that makes sense.
There s no clear way to resolve promises multiple times because since it's resolved it's done. The better approach here is to use observer-observable pattern for example i wrote following code that observes socket client event. You can extend this code to met your need
const evokeObjectMethodWithArgs = (methodName, args) => (src) => src[methodName].apply(null, args);
const hasMethodName = (name) => (target = {}) => typeof target[name] === 'function';
const Observable = function (fn) {
const subscribers = [];
this.subscribe = subscribers.push.bind(subscribers);
const observer = {
next: (...args) => subscribers.filter(hasMethodName('next')).forEach(evokeObjectMethodWithArgs('next', args))
};
setTimeout(() => {
try {
fn(observer);
} catch (e) {
subscribers.filter(hasMethodName('error')).forEach(evokeObjectMethodWithArgs('error', e));
}
});
};
const fromEvent = (target, eventName) => new Observable((obs) => target.on(eventName, obs.next));
fromEvent(client, 'document:save').subscribe({
async next(document, docName) {
await writeFilePromise(resolve(dataDir, `${docName}`), document);
client.emit('document:save', document);
}
});
If you need to change the return value of promise, simply return new value in then and chain next then/catch on it
var p1 = new Promise((resolve, reject) => { resolve(1) });
var p2 = p1.then(v => {
console.log("First then, value is", v);
return 2;
});
p2.then(v => {
console.log("Second then, value is", v);
});
You can write tests to confirm the behavior.
By running the following test you can conclude that
The resolve()/reject() call never throw error.
Once settled (rejected), the resolved value (rejected error) will be preserved
regardless of following resolve() or reject() calls.
You can also check my blog post for details.
/* eslint-disable prefer-promise-reject-errors */
const flipPromise = require('flip-promise').default
describe('promise', () => {
test('error catch with resolve', () => new Promise(async (rs, rj) => {
const getPromise = () => new Promise(resolve => {
try {
resolve()
} catch (err) {
rj('error caught in unexpected location')
}
})
try {
await getPromise()
throw new Error('error thrown out side')
} catch (e) {
rs('error caught in expected location')
}
}))
test('error catch with reject', () => new Promise(async (rs, rj) => {
const getPromise = () => new Promise((_resolve, reject) => {
try {
reject()
} catch (err) {
rj('error caught in unexpected location')
}
})
try {
await getPromise()
} catch (e) {
try {
throw new Error('error thrown out side')
} catch (e){
rs('error caught in expected location')
}
}
}))
test('await multiple times resolved promise', async () => {
const pr = Promise.resolve(1)
expect(await pr).toBe(1)
expect(await pr).toBe(1)
})
test('await multiple times rejected promise', async () => {
const pr = Promise.reject(1)
expect(await flipPromise(pr)).toBe(1)
expect(await flipPromise(pr)).toBe(1)
})
test('resolve multiple times', async () => {
const pr = new Promise(resolve => {
resolve(1)
resolve(2)
resolve(3)
})
expect(await pr).toBe(1)
})
test('resolve then reject', async () => {
const pr = new Promise((resolve, reject) => {
resolve(1)
resolve(2)
resolve(3)
reject(4)
})
expect(await pr).toBe(1)
})
test('reject multiple times', async () => {
const pr = new Promise((_resolve, reject) => {
reject(1)
reject(2)
reject(3)
})
expect(await flipPromise(pr)).toBe(1)
})
test('reject then resolve', async () => {
const pr = new Promise((resolve, reject) => {
reject(1)
reject(2)
reject(3)
resolve(4)
})
expect(await flipPromise(pr)).toBe(1)
})
test('constructor is not async', async () => {
let val
let val1
const pr = new Promise(resolve => {
val = 1
setTimeout(() => {
resolve()
val1 = 2
})
})
expect(val).toBe(1)
expect(val1).toBeUndefined()
await pr
expect(val).toBe(1)
expect(val1).toBe(2)
})
})
What you should do is put an ng-if on your main ng-outlet and show a loading spinner instead. Once your locale is loaded the you show the outlet and let the component hierarchy render. This way all of your application can assume that the locale is loaded and no checks are necessary.
No. It is not safe to resolve/reject promise multiple times. It is basically a bug, that is hard to catch, becasue it can be not always reproducible.
There is pattern that can be used to trace such issues in debug time. Great lecture on this topic: Ruben Bridgewater — Error handling: doing it right! (the part related to the question is around 40 min)
see github gist: reuse_promise.js
/*
reuse a promise for multiple resolve()s since promises only resolve once and then never again
*/
import React, { useEffect, useState } from 'react'
export default () => {
const [somePromise, setSomePromise] = useState(promiseCreator())
useEffect(() => {
somePromise.then(data => {
// do things here
setSomePromise(promiseCreator())
})
}, [somePromise])
}
const promiseCreator = () => {
return new Promise((resolve, reject) => {
// do things
resolve(/*data*/)
})
}

Categories