title: Typescript/JS recursive promises
body: <p>I'm learning Javascript and I am stuck using Promises.</p>
  <p>I'm trying to make a tree like structure from API documentation, where a $ref key in the JSON is replaced with the API object that resides somewhere else in the file. This needs to happen fairly synchronous where I go through the keys of the API object, when I find a $ref it's looked up and replaced in the JSON.</p>
  <p>e.g.</p>
  <pre><code>&quot;apiStorageVersion&quot;: {
      &quot;description&quot;: &quot;...&quot;,
      &quot;properties&quot;: {
        &quot;apiVersion&quot;: {
          &quot;description&quot;: &quot;...&quot;,
          &quot;type&quot;: &quot;string&quot;
        },
        &quot;kind&quot;: {
          &quot;description&quot;: &quot;...&quot;,
          &quot;type&quot;: &quot;string&quot;
        },
        &quot;metadata&quot;: {
          &quot;$ref&quot;: &quot;#/definitions/apiMetaData&quot;,
          &quot;description&quot;: &quot;...&quot;
        },
        &quot;spec&quot;: {
          &quot;$ref&quot;: &quot;#/definitions/apiSpec&quot;,
          &quot;description&quot;: &quot;...&quot;
        },
        &quot;status&quot;: {
          &quot;$ref&quot;: &quot;#/definitions/apiStatus&quot;,
          &quot;description&quot;: &quot;...&quot;
        }
      }
  }
  </code></pre>
  <p>I start of with this function that gets a list of API objects that I consider parents in a sense that these are more important objects in the API. <strong>Definitions</strong> is the content of the file and holds all the API objects.</p>
  <pre><code>function fillObjectTree(parents: string[]) {
      console.log(parents);
      Object.keys(definitions).map(apiTitle =&gt; {
          // if the apiTitle is part of the parents list than we process it
          if (parents.includes(apiTitle)) {
              // Read the children and see if any references are part of the parent
              let par = readChildren(definitions[apiTitle])
              par.then(function (vals) {
                  // Do something with values
              })
          }
      })
  }
  </code></pre>
  <p>Next step is reading the properties of this API object and looking for $ref keys.</p>
  <pre><code>function readChildren(definition: { properties: any; }) {
      return new Promise((resolve, reject) =&gt; {
          // Get the properties of the definition
          let props = definition.properties;
          // Properties are not always present on an object.
          if (props) {
              Object.keys(props).map(propName =&gt; {
                  Object.keys(props[propName]).map(elem =&gt; {
                      if (elem.includes(&quot;$ref&quot;)) {
                          // locationURL = the full url for the reference
                          let locationURL: string = props[propName][elem];
                          // returns the needed value for the URL based on the regex
                          let partialURL: string = locationURL.match('(?&lt;=(\#\/.*\/)).*')[0];
                          readReference(partialURL).then((body) =&gt; {
                              console.log(body);
                              delete definition.properties[propName];
                              definition.properties[propName] = body;  
                          });
                      }
                  })
              })
              resolve(definition);
          } else {
              resolve(definition);
          }
      });
  }
  </code></pre>
  <p>When a reference is found a second function is called that looks in the current file for this object.</p>
  <pre><code>function readReference(apiTitle: string) {
      return new Promise((resolve, reject) =&gt; {
          // Check all the definitions and find a match
          Object.keys(definitions).map(apiDef =&gt; {
              if (apiTitle === apiDef) {
                  readChildren(definitions[apiDef]).then((body) =&gt; {
                      resolve(body);
                  })
              }
          })
      })
  }
  </code></pre>
  <p>So what's going wrong?
  Well the order of operations does not seem to match what I want to happen. The object is not replaced in the JSON and not waited for when executing. I rather not used await or async but keep it to baseline Promises if possible.</p>