Topic describes

person.talk('hello').sleep(3000).talk('world')
Copy the code

Implement a class Person that supports the chained call above: print Hello and print world 3 seconds later.

Problem analysis

Each method must return the current instance. First we initialize the code as follows:

class Person {
  talk(str: string) {
    return this;
  }

  sleep(milSec: number) {
    return this; }}const person = new Person();

person.talk("hello").sleep(3000).talk("world");
Copy the code

Let’s examine the member methods one by one:

  • talk: Simple, in factconsole.log
  • sleep: Also very simple, onesetTimeout

Perfecting the method implementation results in the following code:

class Person {
  talk(str: string) {
    console.log(str);
    return this;
  }

  sleep(milSec: number) {
    setTimeout(() = > {
      console.log("get up");
    }, milSec);
    return this; }}const person = new Person();

person.talk("hello").sleep(3000).talk("world");
Copy the code

Because of the js event loop, the code will output hello and world immediately, and then get up three seconds later, as shown below:

hello
world
Get up after 3 seconds
get up
Copy the code

This topic is essentially a task scheduling problem. Since it is a task scheduling problem, it must be associated with the task queue, and the idea suddenly becomes clear:

  • Instead of executing the concrete logic, the member method puts the concrete logic in a task queue and informs the executor to execute it
  • Define the actuator methodrun, continuously obtain tasks from the task queue and execute them
  • When a new task execution notification is received, if a task is being executed or other tasks are waiting in the queue, the current task is blocked until the previous tasks in the queue are cleared. Otherwise, the task is directly executed

The complete code looks like this:

type Task = () = > Promise<boolean>;

class Person {
  tasks: Task[] = [];
  isRunning: boolean = false;

  talk(str: string) {
    const fn: Task = () = > {
      return new Promise((resolve) = > {
        console.log(str);
        resolve(true);
      });
    };
    this.tasks.push(fn);
    this.run();
    return this;
  }

  sleep(milSec: number) {
    const fn: Task = () = > {
      return new Promise((resolve) = > {
        setTimeout(() = > {
          resolve(true);
        }, milSec);
      });
    };
    this.tasks.push(fn);
    this.run();
    return this;
  }

  run() {
    if (this.isRunning || !this.tasks.length) return;
    this.isRunning = true;
    // Fetch the first task from the queue and execute it
    const task = this.tasks.shift() as Task;
    task().then(() = > {
      this.isRunning = false;
      // Execute the next task
      this.run(); }); }}const person = new Person();

person.talk("hello").sleep(3000).talk("world");
Copy the code

So far, the work is done ~