Sign Up

Sign Up to our social questions and Answers Engine to ask questions, answer people’s questions, and connect with other people.

Have an account? Sign In

Have an account? Sign In Now

Sign In

Login to our social questions & Answers Engine to ask questions answer people’s questions & connect with other people.

Sign Up Here

Forgot Password?

Don't have account, Sign Up Here

Forgot Password

Lost your password? Please enter your email address. You will receive a link and will create a new password via email.

Have an account? Sign In Now

Sorry, you do not have permission to ask a question, You must login to ask a question.

Forgot Password?

Need An Account, Sign Up Here

Please type your username.

Please type your E-Mail.

Please choose an appropriate title for the post.

Please choose the appropriate section so your post can be easily searched.

Please choose suitable Keywords Ex: post, video.

Browse

Need An Account, Sign Up Here

Please briefly explain why you feel this question should be reported.

Please briefly explain why you feel this answer should be reported.

Please briefly explain why you feel this user should be reported.

Sign InSign Up

Querify Question Shop: Explore Expert Solutions and Unique Q&A Merchandise

Querify Question Shop: Explore Expert Solutions and Unique Q&A Merchandise Logo Querify Question Shop: Explore Expert Solutions and Unique Q&A Merchandise Logo

Querify Question Shop: Explore Expert Solutions and Unique Q&A Merchandise Navigation

  • Home
  • About Us
  • Contact Us
Search
Ask A Question

Mobile menu

Close
Ask a Question
  • Home
  • About Us
  • Contact Us
Home/ Questions/Q 2029

Querify Question Shop: Explore Expert Solutions and Unique Q&A Merchandise Latest Questions

Author
  • 61k
Author
Asked: November 26, 20242024-11-26T01:39:09+00:00 2024-11-26T01:39:09+00:00

Implementing State management library – Awai Scenario

  • 61k

Hi there devs! πŸ‘‹

Today we will create one of the most helpful tools for our state management library – Scenario. In this part we will only cover basic, yet most useful strategy – fork.

Fork strategy means that every trigger is handled, thus at the same point of time, few async callbacks may be running, which can lead to potential race conditions, so be careful.

Fork Scenario diagram

If you're curious about all the capabilities of scenario, head straight to Awai docs.

The scenario we will implement should receive a trigger and a callback. Callback will be executed each time scenario is triggered.

Similarly to the tools we created in previous parts, scenario will emit events. Its events will be started, fulfilled and rejected.

Things are getting way more interesting at this point, aren't they?

Scaffold of our tool:

const scenario = (trigger, callback) => {   const events = {     started: new AwaiEvent(),     fulfilled: new AwaiEvent(),     rejected: new AwaiEvent(),   };    const run = () => {     // to be implemented   };    run();    return { events }; }; 
Enter fullscreen mode Exit fullscreen mode

In order to implement the run function we can re-use and modify the code we implemented in previous part, where we were listening to dependencies changes in selector.

Wait, wait, wait! πŸ€” This all sounds great, but what can be a trigger? It can be at least our thennable AwaiEvent. But let's make the tool even more powerful, so that trigger can be any Promise! Do you already see where is it going? 😎

At this point our scenario will have two overloads, which will be enough for implementing most of other utils up until the end of this series.

// AwaiEvent trigger (from previous chapters) scenario(awaiEvent, callback);  // factory that returns a Promise, which will trigger a scenario scenario(() => promise, callback); 
Enter fullscreen mode Exit fullscreen mode

As you can see, trigger can be a function returning a promise. Once promise is resolved, the scenario callback will start.

No more words, let's implement.

const getTriggerPromise = (trigger) => typeof trigger === 'function' ? trigger() : trigger;  const run = () => {   getTriggerPromise(trigger).then((event) => {     events.started.emit({ config, event });      Promise.resolve(callback(event))       .then((result) => {         events.fulfilled.emit({ event, result });       })       .catch((error) => {         events.rejected.emit(error);       });      run();   }); }; 
Enter fullscreen mode Exit fullscreen mode

In getTriggerPromise helper function we check the trigger – if it is a function, we call it and expect it to return a promise, otherwise we just return a trigger (which is, according to our overloads, an AwaiEvent).

It is quite remarkable that we pass the result of a trigger to callback as an argument. Now scenario can play the role of event listener! 🧐

Notice how we wrapped callback call with Promise.resolve. Since our callback may be both sync and async, this Promise.resolve is used to ensure that we have a promise at this place.
If this promise is resolved, it means the callback has successfully returned a value, which we then emit in fulfilled event. If it rejects, we emit rejected error.

I hope you understand everything so far. If not, feel free to ask questions.

Full util code:

const getTriggerPromise = (trigger) => {   return typeof trigger === 'function' ? trigger() : trigger; };  const scenario = (trigger, callback) => {   const events = {     started: new AwaiEvent(),     fulfilled: new AwaiEvent(),     rejected: new AwaiEvent(),   };    const run = () => {     getTriggerPromise(trigger).then((event) => {       events.started.emit({ config, event });        Promise.resolve(callback(event))         .then((result) => {           events.fulfilled.emit({ event, result });         })         .catch((error) => {           events.rejected.emit(error);         });        run();     });   };    run();    return { events }; }; 
Enter fullscreen mode Exit fullscreen mode

Now we can use this tool to listen to any state changes

const nameState = state('John');  setTimeout(() => nameState.set('Andrew'), 1000);  scenario(nameState.events.changed, (name) => {   console.log(`New user name: ${name}`); }); 
Enter fullscreen mode Exit fullscreen mode

But our tool is so powerful, let's write a bit more advanced example. We can notify the API as soon as user name is changed, and let's also add some logging without polluting callback code.

const nameChangeScenario = scenario(nameState.events.changed, async (name) => {   await fetch('/api/user-name', {     method: 'POST',     headers: { 'Content-Type': 'application/json' },     body: JSON.stringify({ name })   }); });  scenario(nameChangeScenario.events.started, () => {   console.log('User name changed. We have started API notifying process.'); });  scenario(nameChangeScenario.events.fulfilled, () => {   console.log('Server has been notified about user name change'); });  scenario(nameChangeScenario.events.rejected, (error) => {   console.log(`There were problems while updating user name. Error: ${String(error)}`); }); 
Enter fullscreen mode Exit fullscreen mode

Yes, we can use scenario to listen to another scenario events! 🀫

Inception

You probably think I forgot about promise factory overload? No way, that's my favorite part.

For better demonstration we need a wait util which just resolves a promise after specified amount of time.

const wait = (delay) => new Promise((resolve) => setTimeout(resolve, delay)); 
Enter fullscreen mode Exit fullscreen mode

Here we go, the easiest fancy scenario, which runs every second:

scenario(() => wait(1000), () => {   console.log('One more second passed'); }); 
Enter fullscreen mode Exit fullscreen mode

Under the hood, we run a trigger function that returns a Promise which will be resolved after one second. Right after that, scenario callback will run (logs text to console), and start scenario listener again, so that we will get a new promise which will be resolved after one second, and so forth.

As the last example, we will combine some promises. So that scenario is triggered either every second, or when name is changed.

scenario(   () => Promise.race([     wait(1000),     nameState.events.changed   ]),   () => {     console.log('This callback is called every second, but also when nameState is changed');   } ); 
Enter fullscreen mode Exit fullscreen mode

Summary

Enjoyed this tutorial? Stay tuned for further parts.
Do you see the potential of this tool? Any ideas how you can use Scenario in your projects? Share your thoughts in comments.

As usual, here is the playground where you can play with the tool we created together.

See you in next chapters!

javascriptopensourcetutorialwebdev
  • 0 0 Answers
  • 0 Views
  • 0 Followers
  • 0
Share
  • Facebook
  • Report

Leave an answer
Cancel reply

You must login to add an answer.

Forgot Password?

Need An Account, Sign Up Here

Sidebar

Ask A Question

Stats

  • Questions 4k
  • Answers 0
  • Best Answers 0
  • Users 2k
  • Popular
  • Answers
  • Author

    ES6 - A beginners guide - Template Literals

    • 0 Answers
  • Author

    Understanding Higher Order Functions in JavaScript.

    • 0 Answers
  • Author

    Build a custom video chat app with Daily and Vue.js

    • 0 Answers

Top Members

Samantha Carter

Samantha Carter

  • 0 Questions
  • 20 Points
Begginer
Ella Lewis

Ella Lewis

  • 0 Questions
  • 20 Points
Begginer
Isaac Anderson

Isaac Anderson

  • 0 Questions
  • 20 Points
Begginer

Explore

  • Home
  • Add group
  • Groups page
  • Communities
  • Questions
    • New Questions
    • Trending Questions
    • Must read Questions
    • Hot Questions
  • Polls
  • Tags
  • Badges
  • Users
  • Help

Footer

Querify Question Shop: Explore Expert Solutions and Unique Q&A Merchandise

Querify Question Shop: Explore, ask, and connect. Join our vibrant Q&A community today!

About Us

  • About Us
  • Contact Us
  • All Users

Legal Stuff

  • Terms of Use
  • Privacy Policy
  • Cookie Policy

Help

  • Knowledge Base
  • Support

Follow

© 2022 Querify Question. All Rights Reserved

Insert/edit link

Enter the destination URL

Or link to existing content

    No search term specified. Showing recent items. Search or use up and down arrow keys to select an item.