loading...

Discussion on: What are you struggling with? (frontend)

Collapse
octaneinteractive profile image
Wayne Smallman

Hi Andi!

I've rebuilt an application in Vue, Node and so on that had been written in PHP.

One part of the application uses Cytoscape, and so far I've not been able to replicate it in JavaScript — it appears to be the asynchronous-ness of JavaScript that's the problem, when compared to PHP.

I tried the official Vue component for Cytoscape, but that appears to be riddled with bugs, and while the plain JavaScript version works with basic JSON data, it won't work with anything that's derived from the API and a database (as explained).

Thoughts welcome!

Collapse
andi23rosca profile image
Andi Rosca Author

Hmm,
I'm not familiar with Cytoscape, but what is preventing you exactly from receiving some json from an API request and then passing it on to the library?

Collapse
octaneinteractive profile image
Wayne Smallman

I didn't want to go into too much detail in case you weren't interested.

First, I have to parse the data into edges and nodes. Then, I use a recursive function:

services.acyclicalBuildingOfGraph = (userID, noteID, visited = [], depth = 0, maxDepth = 5) => { ... }

... to cycle through each node, exploring each link in turn to maxDepth.

In PHP, this is simple, but in JavaScript, it's proving to be a major problem.

Thread Thread
andi23rosca profile image
Andi Rosca Author

I'm still not quite sure where the problem lies. Is it the recursivity? Maybe if you can share the exact piece of code where stuff strats to break down?

Thread Thread
octaneinteractive profile image
Wayne Smallman

First, assetGraph() is called, via the API:

'use strict'

let services = {}

// Notes.

const Notes = require('../models/notes')

let graph = {
  nodes: [],
  edges: []
}

// Methods.

services.assetGraph = async (params) => {

  if (graph.nodes.length > 0)
    graph.nodes = []

  if (graph.edges.length > 0)
    graph.edges = []

  const acyclicalGraph = await services.acyclicalBuildingOfGraph(params.userID, params.noteID)

  console.log("Perspective:assetGraph()", graph)

  return acyclicalGraph

}

services.acyclicalBuildingOfGraph = (userID, noteID, visited = [], depth = 0, maxDepth = 5) => {

  console.log("Perspective:acyclicalBuildingOfGraph()", userID, noteID, visited, depth, maxDepth)

  return new Promise((resolve, reject) => {

    userID = parseInt(userID)
    noteID = parseInt(noteID)

    if ( depth > maxDepth )
      return

    if ( visited.includes(noteID) )
      return
    else
      visited.push(noteID)

    services.getAsset({
      id: noteID
    }).then(asset => {

      let assetInQuestion = asset[0].dataValues

      services.getLinksToAsset({
        user_id: userID,
        note_id: noteID
      }).then(linksTo => {

        graph.nodes.push({
          data: {
            id: parseInt(noteID),
            creation: assetInQuestion.creation,
            modification: assetInQuestion.modification,
            title: assetInQuestion.title,
            depth: depth,
            weight: (maxDepth - depth)
          }
        })

        // console.log("Perspective:acyclicalBuildingOfGraph():node", graph.nodes.length)

        if ( linksTo.length > 0 ) {

          let assetsLinkedTo = linksTo[0].dataValues.NotesLinksFrom

          let i = assetsLinkedTo.length

          assetsLinkedTo.forEach(async link => {

            // console.log("Perspective:acyclicalBuildingOfGraph():iteration", i)

            let linkedNoteId = link.to_asset

            let edgeID = `${noteID}->${linkedNoteId}`

            if ( services.findDuplicateEdgeId('id', edgeID) === false ) {

              graph.edges.push({
                data: {
                  id: edgeID,
                  weight: (maxDepth - depth),
                  source: parseInt(noteID),
                  target: parseInt(linkedNoteId)
                }
              })

            }

            await services.acyclicalBuildingOfGraph(userID, linkedNoteId, visited, depth + 1)

          })

          // console.log(`Perspective:acyclicalBuildingOfGraph():graph:${depth}:`, graph.edges)

          return resolve(graph)

        }

      })

    })

  })

}

services.getAsset = (params) => {
  return Notes.getAsset(params)
}

services.getLinksToAsset = (params) => {
  return Notes.getLinksToAsset(params)
}

services.findDuplicateEdgeId = (field, value) => {

  // console.log("Perspective:findDuplicateEdgeId()", edges)

  if ( graph.edges.length > 0 ) {
    for (const [index, edge] of Object.entries(graph.edges)) {

      // console.log("Perspective:findDuplicateEdgeId():for", index, edge)

      if (edge[field] === value) {
        return index
      }
    }
  }

  return false

}

module.exports = services
Thread Thread
andi23rosca profile image
Andi Rosca Author

I think stuff starts to break down because you return inside the Promise in services.acyclicalBuildingOfGraph. If you don't call resolve or reject in a Promise it will never actually finish.

The problems I see are mostly around the confusion with async code in javascript. I'm assuming you need to use async code because Notes.getLinksToAsset(params) or similar calls are fetching stuff?

Async/await syntax is just syntactical sugar over promises, meaning that when you create an async function, it actually creates a function that returns a promise "under the hood". And when you say return inside of an async function it's like calling the resolve function of the Promise. But it doesn't automatically resolve normal promises.

I'd say you should put empty resolve() statements right before your returns, so that the Promise will actually finish. You can also just do resolve(graph) instead of return resolve(graph).
And at least your code should execute. You can take it from there and see if it actually does what it's supposed to do.

Thread Thread
octaneinteractive profile image
Wayne Smallman

I've narrowed it down to something in Vue rather than Node

Andi, thanks for the time, much appreciated.