This tutorial will implement a LazyMan in a number of ways.

The LazyMan method can be called as follows:


LazyMan('Hank');
/ / output:
// Hi! This is Hank!

LazyMan('Hank').sleep(3).eat('dinner')
/ / output:
// Hi! This is Hank!
// // Wait 3 seconds..
// Wake up after 3
// Eat dinner~

LazyMan('Hank').eat('dinner').eat('supper')
/ / output:
// Hi This is Hank!
// Eat dinner~
// Eat supper~

LazyMan('Hank').sleepFirst(2).eat('dinner').sleep(3).eat('supper')
/ / output:
// // Wait 2 seconds..
// Wake up after 2
// Hi This is Hank!
// Eat dinner~
// // Wait 3 seconds..
// Wake up after 2
// Eat supper~

// And so on
Copy the code

The general idea is to solve the LazyMan problem through the task queue, combined with Promise or async can also be more elegant implementation.

There is another way to solve the LazyMan problem with RxJS and its rich operators.

In order to ensure the reading effect, it is recommended that readers read while hands-on operation, click here to download the source code.

1. Task queue implementation

This pattern is similar to the middleware pattern. The core of this pattern is the next method, which is called every time one method in the queue finishes executing until the other method in the queue is executed.

The setTimeout in the constructor ensures that the queue starts execution in the next event loop, ensuring that all actions in the current chain call are loaded into the queue before the call.

class _LazyMan {
  queue: any[] = [];
  constructor(name: string) {
    this.sayName(name);

    setTimeout(() = > {
      this.next(); })}next() {
    const fn = this.queue.shift();
    fn && fn();
  }

  _holdOn(time) {
    return () = > {
      setTimeout(() = > {
        console.log(`Wake up after ${time} second`)
        this.next()
      }, time * 1000)}}sayName(name) {
    const fn = () = > {
      console.log(`Hi! This is ${name}! `);
      this.next();
    }
    this.queue.push(fn);
  }

  sleep(time: number) {
    this.queue.push(this._holdOn(time));
    return this;
  }

  eat(some: string) {
    const fn = () = > {
      console.log(`Eat ${some}~ `);
      this.next();
    }
    this.queue.push(fn);
    return this;
  }

  sleepFirst(time: number) {
    this.queue.unshift(this._holdOn(time));
    return this; }}const LazyMan = (name: string) = > new _LazyMan(name);

LazyMan('Hank').sleepFirst(2).eat('dinner').sleep(3).eat('supper');
Copy the code

Run to view the effect:

yarn demo01
Copy the code

2. Task queue +Promiseimplementation

Using promises is a neat way to do this, replacing the next call in method one with the natural asynchrony of promises

class _LazyMan {
  queue: any[] = [];
  name: string;
  constructor(name) {
    this.name = name
    this.sayName(name)
    Promise.resolve().then(() = > {
      let sequence = Promise.resolve()
      this.queue.forEach(item= > {
        sequence = sequence.then(item)
      })
    })
  }

  sayName(name) {
    this.queue.push(() = > {
      console.log(`Hi! this is ${name}! `)})return this
  }

  eat(meal) {
    this.queue.push(() = > {
      console.log(`eat ${meal}`)})return this
  }

  _holdOn(time) {
    return () = > new Promise(resolve= > {
      setTimeout(() = > {
        console.log(`Wake up after ${time} second`)
        resolve()
      }, time * 1000)})}sleep(time) {
    this.queue.push(this._holdOn(time))
    return this
  }

  sleepFirst(time) {
    this.queue.unshift(this._holdOn(time))
    return this}}const LazyMan = (name: string) = > new _LazyMan(name);

LazyMan('Hank').sleepFirst(2).eat('dinner').sleep(3).eat('supper');

export{};/ / reference article: https://github.com/fi3ework/blog/issues/36
Copy the code

Run to view the effect:

yarn demo02
Copy the code

3. Task queue +asyncimplementation

This implementation is not very different from the method 2 implementation, but async implementation is more elegant

class _LazyMan {
  queue: any[] = [];
  name: string;
  constructor(name) {
    this.name = name
    this.sayName(name)
    setTimeout(async() = > {for (let todo of this.queue) {
        await todo()
      }
      // The following can also be written
      // for await (let todo of this.queue) {
      // todo()
      // }})}sayName(name) {
    this.queue.push(() = > {
      console.log(`Hi! this is ${name}! `)})return this
  }

  eat(meal) {
    this.queue.push(() = > {
      console.log(`eat ${meal}`)})return this
  }

  _holdOn(time) {
    return () = > new Promise(resolve= > {
      setTimeout(() = > {
        console.log(`Wake up after ${time} second`)
        resolve()
      }, time * 1000)})}sleep(time) {
    this.queue.push(this._holdOn(time))
    return this
  }

  sleepFirst(time) {
    this.queue.unshift(this._holdOn(time))
    return this}}const LazyMan = (name: string) = > new _LazyMan(name);

LazyMan('Hank').sleepFirst(2).eat('dinner').sleep(3).eat('supper');

export{};/ / reference article: https://github.com/fi3ework/blog/issues/36
Copy the code

Run to view the effect:

yarn demo03
Copy the code

4. RxJS implementation

The rXJs-based implementation looks refreshing, but requires readers to have a good foundation in RxJS.

RxJS’s natural support for asynchrony and rich operators make this very easy.

In implementing LazyMan, the core is the concatAll operator, which collects all observables and subscribes to the next one when the current one completes.

import { from.of } from 'rxjs';
import { map, concatAll, delay } from 'rxjs/operators';

class _LazyMan {
  tasks: any[] = [];

  constructor(name: string) {
    this.sayName(name);

    setTimeout(() = > {
      from(this.tasks).pipe(
        map(item= > {
          if (item.timeout) {
            return of(item).pipe(delay(item.timeout))
          }
          return of(item)
        }),
        concatAll()
      ).subscribe(res= > res.fn())
    })
  }

  sayName(name) {
    this.tasks.push({
      fn: () = > console.log(`Hi! This is ${name}! `)}); }sleep(time: number) {
    this.tasks.push({
      timeout: time * 1000.fn: () = > console.log(`Wake up after ${time} second`)});return this;
  }

  eat(some: string) {
    this.tasks.push({
      fn: () = > console.log(`Eat ${some}~ `)});return this;
  }

  sleepFirst(time: number) {
    this.tasks.unshift({
      timeout: time * 1000.fn: () = > console.log(`Wake up after ${time} second`)});return this; }}const LazyMan = (name: string) = > new _LazyMan(name);

LazyMan('Hank').sleepFirst(2).eat('dinner').sleep(3).eat('supper');
Copy the code

Run to view the effect:

yarn demo04
Copy the code

The full code for the above example can be viewed here. If you think it’s a good one, you can give me a star. Thanks for reading.

A link to the

How to implement a LazyMan?

Do you know how to write LazyMan?

LazyMan’s Rxjs implementation