I have this sort of lazy initialization code in my program:
let user = null;
let getUser = async () => {
if(!user) {
user = await getUserSomehow();
}
return user;
};
I understand that it is not safe, due to a possible race condition if I have the next code:
// one place of the program
let u1 = await getUser();
...
// another place of the program running during getUserSomehow() for u1 still hasn't finished
let u2 = await getUser();
getUserSomehow()
will be called two times instead of one.
How to avoid this situation?
Lazy initialization of an object means that its creation is deferred until it is first used. (For this topic, the terms lazy initialization and lazy instantiation are synonymous.) Lazy initialization is primarily used to improve performance, avoid wasteful computation, and reduce program memory requirements.
Net Framework 4.0 to provide a thread-safe way to implement lazy initialization. You can take advantage of this class to defer the initialization of resource-intensive objects in your application. When you use the Lazy<T> class, you need to specify the type of object you intend to create lazily in the type argument.
While lazy loading delays the initialization of a resource, eager loading initializes or loads a resource as soon as the code is executed. Eager loading also involves pre-loading related entities referenced by a resource.
An async function is a function declared with the async keyword, and the await keyword is permitted within it. The async and await keywords enable asynchronous, promise-based behavior to be written in a cleaner style, avoiding the need to explicitly configure promise chains.
When called for the first time, assign a Promise instead, and on further calls, return that Promise:
let userProm = null;
let getUser = () => {
if (!userProm) {
userProm = getUserSomehow();
}
return userProm;
};
Even better, scope userProm
only inside getUser
, to be safer and clear:
const getUser = (() => {
let userProm = null;
return () => {
if (!userProm) {
userProm = getUserSomehow();
}
return userProm;
};
})();
const getUserSomehow = () => {
console.log('getting user');
return Promise.resolve('data');
};
const getUser = (() => {
let userProm = null;
return () => {
if (!userProm) {
userProm = getUserSomehow();
}
return userProm;
};
})();
(async () => {
const userProm1 = getUser();
const userProm2 = getUser();
Promise.all([userProm1, userProm2]).then(() => {
console.log('All done');
});
})();
Your existing code happens to be safe, because the assignment to user
will occur before the first call of getUser
finishes:
const getUserSomehow = () => {
console.log('Getting user');
return Promise.resolve('data');
};
let user = null;
let getUser = async() => {
if (!user) {
user = await getUserSomehow();
}
return user;
};
(async () => {
let u1 = await getUser();
let u2 = await getUser();
console.log('Done');
})();
But it wouldn't be if the Promises were initialized in parallel, before one of them was await
ed to completion first:
const getUserSomehow = () => {
console.log('Getting user');
return Promise.resolve('data');
};
let user = null;
let getUser = async() => {
if (!user) {
user = await getUserSomehow();
}
return user;
};
(async() => {
let u1Prom = getUser();
let u2Prom = getUser();
await Promise.all([u1Prom, u2Prom]);
console.log('Done');
})();
As shown above, assigning the Promise to the persistent variable (instead of await
ing the value inside getUser
) fixes this.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With