Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

What is a canonical safe way of async lazy initialization in javascript?

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?

like image 430
Arsenii Fomin Avatar asked Nov 11 '19 06:11

Arsenii Fomin


People also ask

Which one is true about lazy initialization of objects?

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.

How would you accomplish lazy initialization in net?

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.

What is lazy and eager initialization in spring?

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.

What is async await and how does it work Javascript?

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.


1 Answers

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 awaited 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 awaiting the value inside getUser) fixes this.

like image 143
CertainPerformance Avatar answered Oct 27 '22 13:10

CertainPerformance