If you are interested in this library, please welcome the Star or request Issue and can also become a contributor to this library. (I would appreciate it if someone could help me translate English files or write test cases)

Instead of cancelling promises, make multiple identical promises into one! The function will still be executed and return a value, but it will not be regenerated into redundant promises.

Imagine the following two scenarios

1

Sometimes, there are multiple sections on the same page, as shown in the figure below.

There are some duplicate data in each part, such as the green part, which actually corresponds to the request for the same content in the back end.

At this point, if we take the approach of making a request for each component block of the module, it is obvious that the three requests that can share the return value are duplicated.

For these three duplicate requests, is there a way to make only one request and then share the same return value for all three?

2

Suppose you have a component that provides partial refresh or load capabilities.

Sometimes when a user hits refresh, they just click on it more than once, maybe several times, for some unknown reason, resulting in multiple requests being sent directly.

Is it possible to consolidate multiple requests and send only one request?

once-init

You can certainly design your own complex logic that encapsulates a relatively safe component. Or, you could try once-init?

github npm

Promise Function Init Once, Use Everywhere.

A Promise function that will only be initialized once.

The Promise function initialization is performed the first time the object is called, and the initialization is never performed again.

The same Promise is never executed twice at the same time.

Compressed size

commitment

  1. OnceInitencapsulatedPromise FunctionWill never be executed twice at the same time.
  2. If the last onePromise FunctionThe next one is called without completionPromise FunctionSo the next onePromise FunctionWill share the last onePromise FunctionPromise.

The sample

Suppose there is an AXIos Promise request that returns a value of type number and value 777.

const requestNumber = async() = > {const res: AxiosResponse<number> = await axiosInstance.get("/api/number");
  return res.data;
};
Copy the code

You can encapsulate the Promise function with oi

const oiInstance = oi(requestNumber);
Copy the code

Now, you can call this instance from anywhere.

init

Suppose you have two methods functionA and functionA, both of which need to send this request.

async function functionA() {...const res = awaitoiInstance.init(); . }async function functionB() {...const res = awaitoiInstance.init(); . }Copy the code

And you need to use both methods in a file.

async function functionC() {
  await functionA();
  await functionB();
}

function functionD() {
  functionA();
  functionB();
}
Copy the code

In the case of functionC, oiInstance will save the result of the Promise execution after the first init, and no Promise request will be issued after the init is executed.

For functionD, the API request is sent only once, and both the RES in functionA and functionB will wait for the return value of the same request, and no duplicate requests will be sent.

target

Target can get the return value synchronously.

function functionE() {...constres = oiInstance.target; . }Copy the code

If the initialization is completed before the target is obtained, the target value is the return value of the Promise; otherwise, the target value is undefined. For example,

const res = oiInstance.target; // undefined
Copy the code
await oiInstance.init();

const res = oiInstance.target; // [Return Value] 777
Copy the code

Note that even though the fetch is synchronous, once-init still thinks you need to make the request at this point, so calling the target property will also start the initialization.

In the following example, we assume that the API request duration is 10s. In the following example, the request is sent on the first line.

const res = oiInstance.target; // undefined
/** Promise has been executed. */
setTimeout(async() = > {const resAfter = oiInstance.target; // [Return Value] 777
  const intAffter = await oiInstance.init(); // [Return Value] 777 , Promise will not be executed again.
  /** Since The Promise has been executed before, it will not be executed again. */
}, 10001);
Copy the code

As with executing init simultaneously, if the target property is accessed before fetching init and the Promise request does not end, Init will simply wait for the last Promise to end and return the return value of the previous Promise.

The following example will help you understand.

const res = oiInstance.target; // undefined
setTimeout(async() = > {const resAfter = oiInstance.target; // undefined
  const intAffter = await oiInstance.init(); // [Return Value] 777
  /** Since The Promise has been executing it will not be executing again. */
  /** After About 8000ms, The Value will be return by the first promise done */
}, 2000);
Copy the code

Init will wait for the return value from the previous Promise function execution, and since init was executed after 200ms, it will only have to wait about 800ms to get the return value.

defaultValue

Using the target attribute usually requires default values, whereas the second argument to OI defines default values for your Promise.

const defaultValue = -1;
const oiInstance = oi(requestNumber, defaultValue);

const ans = oiInstance.target; // -1
Copy the code

refresh

If you want to update the value of an instance, you need to call Refresh.

Suppose the first load value is 777 and the value after the refresh is 888.

const ans = await oiInstance.init(); // [Retrun Value] 777
const ansAfterRefresh = await oiInstance.refresh(); // [Retrun Value] 888
Copy the code

After the refresh, the values obtained by calling init and target become the new values.

oiInstance.target; // undefined
await oiInstance.init(); // [Promise Retrun Value] 777
oiInstance.target; / / 777
await oiInstance.refresh(); // [Promise Retrun Value] 888
/** Promise will not be exectued */
oiInstance.target; / / 888
await oiInstance.init(); / / 888
Copy the code

You can use Refresh directly to perform initialization, and it works just as well as init.

oiInstance.target; // undefined
await oiInstance.refresh(); // [Promise Retrun Value] 777
oiInstance.target; / / 777
await oiInstance.refresh(); // [Promise Retrun Value] 888
oiInstance.target; / / 888
Copy the code

If the synchronization calls REFRESH twice, they wait for the return value of the same request and do not send a duplicate request.

async function functionA() {
  console.log("A".await oiInstance.refresh());
}
async function functionB() {
  console.log("B".await oiInstance.refresh());
}
functionA(); // 'A', [Promise Retrun Value] 777
functionB(); // 'B', [Promise Retrun Value] 777
/** only one promise is executed */
/** functionA and functionB share A same promise and promise return value */
Copy the code

We still assume that the duration of the API request is 10s === 10000ms.

oiInstance.refresh();
setTimeout(async() = > {await oiInstance.refresh();
}, 2000);
/** After 10000ms, two refresh will be exected at the same time */
Copy the code

If the asynchrony calls Refresh twice in succession, send two requests.

async function functionA() {
  console.log("A".await oiInstance.refresh());
}
async function functionB() {
  console.log("B".await oiInstance.refresh());
}
await functionA(); // 'A', [Promise Retrun Value] 777
await functionB(); // 'B', [Promise Retrun Value] 888
/** Two different promises were executed */
Copy the code

If your logic seems too complicated, at least remember that Promise functions wrapped in OnceInit will never be executed twice at the same time.

In addition, once-init provides other apis to meet more requirements, but these are its main functions. See Github for once-Init for more information.

HELP

I will also release a version of the Vue3-Composition Api. (Scheduled)

export abstract class RefOnceInit<T, G = T> extends OnceInit<
  Ref<T | undefined>,
  G
> {
  loading = ref<boolean>(false);
  protected abstract factory(raw: G, observe: Ref<T | undefined>): void;
  constructor(defaultValue?: T) {
    const refed = ref<T>();
    refed.value = defaultValue;
    super(refed);
    this.onLoading((event) => {
      this.loading.value = event;
    });
  }
}
Copy the code

So RefOnceInit is a reactive object. Check the source code for the OnceInit class.

When the request completes, it triggers a change to the page UI.