npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

@perceived/bisect

v0.0.4

Published

Bisect your async job into two steps, first perform work in background and then commit the work.

Downloads

9

Readme

bisect

Bisect your async task into two steps, first perform async work in background and then apply the effect of work.

For example while you are fetching some data and updating view with new data. You can break task in following to improve perceived experience.

  1. fetch the data (background work)
  2. Apply the new data on your view (effect of work)

This is more of a pattern than a library. Library is pretty small.

usage

const start = bisect({
  background(...args) {
    // Async work which happens on background and does not have any effect on view or state. like just making fetch call
    // This method should return a promise.
    return fetch(url);
  },
  effect(data) {
    // this is where the effect from the async work is applied. For example you want to update view or state based on api response.
    updateView(data);
  },
  // Thresold to expire/discard background work by async task if it is not applied before a expiry period.
  // Default to Math.MAX_SAFE_INTEGER (never expire)
  expiry: 1000,
});

Then you can use it like

let apply;

// this could be anything, not necessarily mouseenter on button
button.addEventListener("mouseenter", () => {
  apply = start(someParam);
});

button.addEventListener("click", () => {
  apply();
});

Why bisect my async task?

Lets understand with an example. Let's say on a ecommorce site you have the product list. On click of a product you load data and show the detail on side drawer.

A very basic implementation would look something like this.

function showProductDetail(productId) {
  return fetch(`/product/details/${productId}`)
    .then((response) => response.json())
    .then((data) => {
      const productDetail = formatData(data);
      openProductDetailDrawer(productDetail);
    });
}

Ignore the race conditions that can happen on this. In this we are just focusing on fetching a data and displaying them on a view.

Now on a basic flow, the User Interaction and Code Flow would be something like this.

  • User clicks on product.
  • You display a loader.
  • You call showProductDetail method with that product id.
  • The method fetches the data and once available it shows the data on the view.
  • You clear the loader.

Let's say API takes 1s to load the data and openProductDetailDrawer takes 50ms to show it in the view. So when user clicks a product it takes 1050ms for a user to present the result. Lets call that a waiting time of 1050ms.

We can definetly improve this experience. If we start loading the data before hand lets say when user hovers over the card. We can reduce the overall waiting time as the waiting time starts after user clicks the product, and we have started loading it before hand.

But we can't just call showProductDetail on hover, as we can just predict the user intention but we are not sure if they will actully click it.

But, we can break this task into two parts.

  1. Fetch the product detail when user hovers on the product. (Just the fetch, which doesn't have any effect on view).
  2. Apply the data (call openProductDetailDrawer) on view only when user clicks.

Let's refactor this code into this two methods.

function fetchProductDetail(productId) {
  return fetch(`/product/details/${productId}`).then((response) =>
    response.json()
  );
}

function showProductDetail(data) {
  const productDetail = formatData(data);
  openProductDetailDrawer(productDetail);
}

Then we can call the fetchProductDetail on hover and call showProductDetail on click with the response data of fetch. The usage may look something like this.

let fetchPromise;

card.addEventListener("mouseenter", () => {
  fetchPromise = fetchProductDetail(productId);
});

card.addEventListener("click", () => {
  fetchPromise.then(showProductDetail);
});

There might be some other cases like discard promise if it happened before a threshold time. Bisect provides a small util for writing it in cleaner way.

const showProductDetail = bisect({
  background(producdId) {
    return fetch(`/product/details/${productId}`).then((response) =>
      response.json()
    );
  },
  effect(data) {
    const productDetail = formatData(data);
    openProductDetailDrawer(productDetail);
  },
  expiry: 3000, // default to never expire
});
let applyTask;

card.addEventListener("mouseenter", () => {
  applyTask = showProductDetail(productId);
});

card.addEventListener("click", () => {
  applyTask();
});