Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

How to chain thens in usePromise? #414

Open
wizardnet972 opened this issue Jul 5, 2020 · 5 comments
Open

How to chain thens in usePromise? #414

wizardnet972 opened this issue Jul 5, 2020 · 5 comments

Comments

@wizardnet972
Copy link

wizardnet972 commented Jul 5, 2020

How to chain .thens functions and adjust the loading property to be true after all the thens complete?

I have a function getItems that return array with ids after 20 seconds (like http).

export function timeout(ms) {
  return new Promise(resolve => setTimeout(resolve, ms));
}

const getItems = () => timeout(20 * 1000).then(() => [{ id: 1 }, { id: 2 }]);

and I call this function via usePromise:

const useApi = () => {
const api = usePromise(() => getItems(), { lazy: true });
return api;
};

In the setup function I call to useApi and pass the loading to the template, and I get the r with the data.

const App = {
  template: `
  <div>
    <h1>im app {{loading}}</h1>
  </div>
  `,
  setup() {
    const { exec, loading } = useApi();

    exec().then(r => {
      console.log({ r });
    });

    return { loading };
  }
};

But what if I want to add other function without change the useApi and I want loading to not change until all of the then complete.

exec().then(async r => {
  console.log({ r });  // <--- the `loading` is true :(
  await timeout(20 * 1000);  
 // <-- here, now the `loading` should be true.
});

I want to wait another 20 seconds. but after the first then complete it set loading to ture.

@pikax There is something I can do to make it works?
my example on codesandbox.io

@pikax
Copy link
Owner

pikax commented Jul 5, 2020

When you do .then it will create a new promise, making the useApi not be aware of it.

What you can do is passing the function to the exec:

const useApi = () => {
  const api = usePromise(p => getItems().then(p), { lazy: true });

  return api;
};


//setup
    exec(async r => {
      console.log({ r, loading });

      await timeout(20 * 1000);
    });

codesandbox

@wizardnet972
Copy link
Author

wizardnet972 commented Jul 5, 2020

@pikax thanks for your answer.

But what I think of is to use useApi as it is. and sometimes I want to do something after, and I want to control it in the host function.

I think about something like this to catch the error or the promise. instead of try..catch..finally .

export const scope = (fn) => (req, res, next, ...args) => Promise.resolve(fn(req, res, next, ...args)).catch(next); // this is how I handle promise with nodejs

This is mean something like Promise/finally:

 const promise = new Promise((res) => { setTimeout(() => res(), 10* 1000) });
  ...
  Promise.resolve(promise)
   .catch(e => )
   .finally(() => loading = false)
  ...
 promise.then(r => ...);

so here you will get the catch when error, and somebody can hook into the original promise. (and the finally always happens)

@pikax
Copy link
Owner

pikax commented Jul 5, 2020

I don't think you can hook to the original promise.

export function timeout(ms) {
  return new Promise(resolve => setTimeout(resolve, ms));
}

const getItems = () => timeout(1000).then(() => [{ id: 1 }, { id: 2 }]);

function myPromise(p) {
  const loading = ref(true);
  const promise = Promise.resolve(p).finally(() => (loading.value = false));
  return {
    promise,
    loading
  };
}

export default defineComponent({
  name: "App",

  setup() {
    const promise = myPromise(getItems());

    promise.promise.then(async x => {
      console.log("resolved", x);
      await timeout(2000);
      console.log("rrr ", promise.loading.value); // this  is always false
    });

    return promise;
  }
});

sandbox

That's a javascript behaviour, if you can provide a working example of hooking into a promised already created I really appreciate it :)

@wizardnet972
Copy link
Author

wizardnet972 commented Jul 5, 2020

@pikax Here what I suggest. I rewrite the usePromise (with same signature) but a different way to hook the promise.

What do you think about it? can I send PR?

sandbox

<template>
  <div id="app">Loading: {{loading}}</div>
</template>

<script>
import { defineComponent, toRefs, reactive } from "@vue/composition-api";

export function timeout(ms) {
  return new Promise(resolve => setTimeout(resolve, ms));
}

const getItems = () =>
  timeout(1000)
    .then(() => {
      console.log("in getItems..");
    })
    .then(() => [{ id: 1 }, { id: 2 }]);

const usePromise = (fnOrPromise, options = { lazy: false }) => {
  const state = reactive({
    loading: false,
    error: null,
    promise: typeof fnOrPromise === "function" ? fnOrPromise() : fnOrPromise,
    exec: () => {
      state.loading = true;
      state.error = null;

      const executePromise = Promise.resolve(state.promise)
        .catch(e => (state.error = e))
        .finally(() => {
          console.log("in finally");
          state.loading = false;
        });

      return executePromise;
    }
  });

  if (!options.lazy) {
    state.exec();
  }

  return toRefs(state);
};

export default defineComponent({
  name: "App",

  setup() {
    console.clear();
    console.log("in setup");

    const { exec, loading, promise } = usePromise(() => getItems(), {
      lazy: true
    });

    promise.value.then(r => {
      console.log({ r });
      console.log({ l: loading.value });
    });

    exec.value().then(t => {
      console.log({ t });
      console.log({ l: loading.value });
    });

    return { loading };
  }
});
</script>


@pikax
Copy link
Owner

pikax commented Jul 6, 2020

That usePromise actually acts a bit different from what usePromise currently behaves.

The value is only resolved once, no matter how many times you call exec.value it will always resolve the first promise (won't call the factory anymore)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants