0

I'm trying to Create a Promise that resolves when variable is not undefined.

Code example

https://codesandbox.io/s/quizzical-feynman-ktvox?file=/src/index.js

let fetchedPromise = new Promise((resolve, reject) => {
  const check = ()=>{
    setTimeout(() =>{
    console.log("checking")
    if (dataFetched) resolve(dataFetched);
    else check()
    }, 100);
  }
  check()
});

const waitForDataFromAnotherComponent = async () => {
  let result = await fetchedPromise;
  console.log("Result: ", result);
};

const assignData = () => {
  setTimeout(() => {
    dataFetched = 1000;
    console.log(dataFetched);
  }, 5000)
};

waitForDataFromAnotherComponent();
assignData();

This works but I find it inefficient as it's callstack prone and seems wrong.

Other non-working solutions I've tried:

//-------------SOLUTION 1
let fetchedPromise = new Promise((resolve, reject) => {
  const check = ()=>{
    if (dataFetched) resolve(dataFetched);
    else check()
  }
  check()
});

//--------------------SOLUTION 2
let fetchedPromise = new Promise((resolve, reject) => {
   if (dataFetched) resolve(dataFetched);
});

Scenario

I need a function like solution 3 that doesn't rely on setTimeout

deceze
  • 510,633
  • 85
  • 743
  • 889
  • [MutationObserver](https://developer.mozilla.org/en-US/docs/Web/API/MutationObserver) is likely what you want. – Sirko Aug 28 '20 at 10:03
  • There's no way that `assignData` may accept a callback or return a promise itself…!? – deceze Aug 28 '20 at 10:17
  • @Sirko how can I integrate mutationObserver in my Code? ` const callback = function(observer) { if(observer) console.log("Observer is ", observer) } const observer = new MutationObserver(callback) observer.observe(dataFetched); ` this returns node error on observer – Michelangelo De Francesco Aug 28 '20 at 10:23
  • `assignData` is just a *fake* function that represents the fact that the variable is changed. In my "real" code there is a function that assigns a value to `assignData` – Michelangelo De Francesco Aug 28 '20 at 10:27
  • It's clear that this is an example for example's sake. I want to make sure you're not barking up the wrong tree entirely and are missing the easy option of something like `assignData().then(...)`. – deceze Aug 28 '20 at 10:28
  • In order to make my structure clear, i'll explain it to you: i have a useFetch Hook (a function if you're not familiar with react) that fetches api, assigns data fetched to a variable(a useState variable) and returns the data itself. I have a React Context (it's like a global object) that registers these api calls. if 2 useFetch functions are called at the same time with the same signature(same input in the function), i have a method that checks if there's an api in 'pending fetch' state so that one of the 2 usefetch function doesn't fetch data but *waits* for the other function – Michelangelo De Francesco Aug 28 '20 at 10:36
  • As i can see MutationObserver is for DOM changes, instead, maybe [Proxy](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Proxy) could get the work done – Michelangelo De Francesco Aug 28 '20 at 10:41
  • I would think there's a better, perhaps React-specific way to handle this rather than trying to observe an individual variable. I can't give any detailed advice on that though without knowing more details, and even then I'm not a React guy… – deceze Aug 28 '20 at 11:35

1 Answers1

0

Solved by using Javascript Proxy

Basically I assign a Proxy Object to dataFetched that listens to changes. I re-create the function of listening due to the fact that it must include resolve()

let dataFetched
let x = {
  aListener: function (val) {},
  set a(val) {
    dataFetched = val;
    this.aListener(val);
  },
  get a() {
    return dataFetched;
  },
  registerListener: function (listener) {
    this.aListener = listener;
  }
};

let fetchedPromise = new Promise((resolve, reject) => {
  x.registerListener(function (val) {
    console.log("yeyyyyyyyyyyyyyyy");
    if (dataFetched) resolve(dataFetched);
  });
});

const waitForDataFromAnotherComponent = async () => {
  let result = await fetchedPromise;
  console.log("Result: ", result);
};
const assignData = async () => {
  await new Promise((resolve, reject) =>
    setTimeout(() => {
      x.a = 1000;
      console.log(dataFetched);
      resolve(dataFetched);
    }, 1000)
  );
};

waitForDataFromAnotherComponent();
assignData();

EDIT

Actually it's possible to externalize the resolve() function of the promise but with some downsides as stated here

example

let dataFetched
var promiseResolve, promiseReject;

let x = {
  aListener: function (val) {
    if (dataFetched) promiseResolve(dataFetched);
},
  set a(val) {
    dataFetched = val;
    this.aListener(val);
  },
  get a() {
    return dataFetched;
  },
  registerListener: function (listener) {
    this.aListener = listener;
  }
};

let fetchedPromise = new Promise((resolve, reject) => {
 promiseResolve = resolve;
  promiseReject = reject;
});