Turn variable into literal string?

Hello ,
I’m having a prob putting dialog.showOpenDialog result into fs.readFileSync(FilePaths.toString()).toString() .
Ie., from variable FilePaths into fs.readFileSync() , because fs. requires a ‘string’ .
I am getting error:
TypeError: Cannot read property ‘toString’ of undefined
The following doesn’t work:
fs.readFileSync(FilePaths.toString()).toString();

  ipcMain.handle('openBtn' , (event) => { 
	  console.log("MAIN ipcMain.handle('openBtn" + event)
const files = dialog.showOpenDialog({
          defaultPath:app.getPath("desktop") ,
		  buttonLabel:'Select File To Edit'
	  }).then((result) => { FilePaths = result.filePaths[0] }) 
         .then((result) => { console.log("FilePaths = " + FilePaths) })	  
		 if (!files) { return; } 
const  content = fs.readFileSync(FilePaths.toString()).toString();
		 console.log(content) ;
}) 

Thanks for your Help…

Hi @vmars316, a promise (as returned by showOpenDialog()) will asynchronously resolve at some point in the future, but you’re trying to access FilePaths right away; to fix this, chain readFileSync() to the promise as well (no need to assign to a variable in the global scope then):

ipcMain.handle('openBtn', () => {
  dialog
    .showOpenDialog({
      defaultPath: app.getPath('desktop'),
      buttonLabel: 'Select File To Edit'
    })
    .then(result => result.filePaths[0])
    .then(filePath => { 
      const content = fs.readFileSync(filePath)
      console.log(content)
    })
})

I’d also avoid synchronously reading files though; using util.promisify(), the non-blocking asynchronous version can nicely be chained as well:

const fs = require('fs')
const util = require('util')
const readFile = util.promisify(fs.readFile)

ipcMain.handle('openBtn', () => {
  dialog
    .showOpenDialog({
      defaultPath: app.getPath('desktop'),
      buttonLabel: 'Select File To Edit'
    })
    .then(result => result.filePaths[0])
    .then(filePath => readFile(filePath))
    .then(content => {
      // Do something...
      console.log(content)
    })
    .catch(console.error)
})
2 Likes

Thank you very much !
Yeah , still having trouble with Promises syntax .

Happy to help! BTW you might also use async / await for a more synchronous-looking syntax:

ipcMain.handle('openBtn', async () => {
  const result = await dialog.showOpenDialog({
    defaultPath: app.getPath('desktop'),
    buttonLabel: 'Select File To Edit'
  })

  const filePath = result.filePaths[0]

  if (!filePath) {
    return
  }

  const content = await readFile(filePath)

  console.log(content)
})

… but as always with syntactic sugar, it won’t hurt to get acquainted with what’s happening behind the scenes first. :-)

Completely agree, so much so, I thought promisify would be an interesting function to look into :slight_smile:

The implementation could be as follows.

Longform with breakdown


// Promisify is a wrapper, that takes a 'given function'
// and returns another function which when invoked will return a promise.

// The promise initiates a call to the previously 'given function' passing in arguments and
// a callback with set parameters (error, /* and */ value)

// If the callback is given a first argument which evaluates to 'true'
// — in other words an error — the promise is rejected with that argument
// Otherwise if the first argument evaluates to 'false'
// the second argument 'value' is resolved — 'Happy Days'

function promisify (func) {
  return function(...args) {

    return new Promise(function(resolve, reject){
      func(...args, function(error, value) {
        if (error) reject(error)
        else resolve(value)
      })
    })
  }
}

In shortform

const promisify = (fn) => (...args) =>
  new Promise((resolve, reject) => {
    fn(...args, (error, value) => (error ? reject(error) : resolve(value)))
  })

Usage example I am basing on the examples on https://javascript.info/promisify, but with the more generic promisify above.

./scripts/dummy-script.js

GlobalData = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]

index.html

<!DOCTYPE html>
<html lang='en'>
<head>
  <meta charset='UTF-8'>
  <meta http-equiv='X-UA-Compatible' content='IE=edge'>
  <meta name='viewport' content='width=device-width, initial-scale=1.0'>
  <title>Promisify</title>
</head>
<body>
  <script>
    const promisify = (fn) => (...args) =>
      new Promise((resolve, reject) => {
        fn(...args, (error, value) => (error ? reject(error) : resolve(value)))
      })

    // example taken from https://javascript.info/promisify
    const loadScript = (src, callback) => {
      const script = document.createElement('script')
      script.src = src

      script.onload = () => callback(null, script)
      script.onerror = () => callback(new Error(`Script load error for ${src}`))

      document.head.append(script)
    }

    // promisify loadScript
    const loadScriptPromisified = promisify(loadScript)

    // successfully resolve
    loadScriptPromisified('./scripts/dummy-script.js')
      .then(script => console.log(GlobalData)) // [1, 2, 3, ...9, 10]
      .catch(console.log)

    // typo in path
    loadScriptPromisified('./scripts/dummyScrip.js')
      .then(script => console.log(GlobalData))
      .catch(console.log)  // Error: Script load error for ./scripts/dummyScrip.js

  </script>
</body>
</html>
1 Like

How did you get away with using (filePath) instead of a string ?
I always get errors with out literal

 'c:\.........' .

filePaths is a keyword in
https://www.electronjs.org/docs/api/dialog#dialogshowopendialogbrowserwindow-options
But where did filePath come from ?
Thanks

It’s just the name of the first (and only) parameter of the callback function; we could have named it differently but filePath seemed appropriate.

Here’s how the code looks when standard functions instead of arrow-functions.

    .then(function (result) {
        return result.filePaths[0])
    }.then(function (filePath) { 
      const content = fs.readFileSync(filePath)
      console.log(content)

That’s partly why I prefer enclosing even single parameters in parenthesis, as it helps to avoid such confusion.

    .then(
      (result) => result.filePaths[0]
    ).then(
      (filePath) => {
        const content = fs.readFileSync(filePath)
        console.log(content)

Ok , Thanks

This topic was automatically closed 91 days after the last reply. New replies are no longer allowed.