Asynchrony issue on Electron

2019-08-23 10:47发布

问题:

I'm currently working on an Electron.js app and I'm stuck on an asynchrony problem. I have three function (downdloadFile()) inside three if statements to be executed in sequence but they run asynchronously; I already tried using async/await but it didn't worked. Here's my code:

ipcMain.on('play',(event,payload) => {
        launcherConfig.savedRam = payload.savedRam;
        launcherConfig.savedMaxPermSize = payload.savedMaxPermSize;
        if(payload.selectedPacket) {
            //FIRST IF
            if (!fs.existsSync(launcherDir + "\\natives")) {
                downloadFile("http://soulnetwork.it/launcher/natives.zip", launcherDir + "\\natives.zip", true, 'natives');
            }
            //SECOND IF
            if (!fs.existsSync(launcherDir + "\\bin")) {
                downloadFile("http://soulnetwork.it/launcher/bin.zip", launcherDir + "\\bin.zip", true, 'bin');
            }
            //THIRD IF
            if (launcherConfig.installed_modpacks.includes(payload.selectedPacket)) {
                launchMinecraft(payload.selectedPacket);
                saveConfig();
            } else {
                downloadFile(`http://soulnetwork.it/launcher/modpacks/${payload.selectedPacket}.zip`, launcherDir + "\\modpacks\\" + payload.selectedPacket + '.zip', true, payload.selectedPacket, launchMinecraft);
                launcherConfig.installed_modpacks.push(payload.selectedPacket);
                saveConfig();
            }
        }
}

function downloadFile(file_url , targetPath, showProcess, packetName, callback){
// Save variable to know progress
var received_bytes = 0;
var total_bytes = 0;

var req = request({
    method: 'GET',
    uri: file_url
});

var progressWindow = null;

if(showProcess){
    progressWindow = new BrowserWindow({width: 300, height: 60, title: `Downloading ${packetName}`,icon: '../public/images/sn.png'})
    progressWindow.setProgressBar(0.0);
    progressWindow.loadURL('file://' + __dirname + '/views/download.ejs');
    progressWindow.setMenu(null);
}

var out = fs.createWriteStream(targetPath);
req.pipe(out);

req.on('response', function ( data ) {
    // Change the total bytes value to get progress later.
    total_bytes = parseInt(data.headers['content-length' ]);
});

req.on('data', function(chunk) {
    // Update the received bytes
    received_bytes += chunk.length;
    if(showProcess)
        showProgress(progressWindow,received_bytes, total_bytes);
});

req.on('end', function() {
    if(showProcess)
        progressWindow.close();
    if(targetPath.includes('.zip')){
        var zip = new archiver(targetPath);
        zip.extractAllTo(targetPath.substr(0,targetPath.length-4-packetName.length));
        fs.unlinkSync(targetPath);
    }
    if(callback)
        callback(packetName);
    console.log("RETURN");
    return;
});
}

Using the callback system creates so much caos and it is also redundant, I was hoping for another solution. Thank you for your time!

回答1:

Where you currently console.log("RETURN") you can resolve a Promise which is returned by downloadFile. Then you can simply await the calls in your if branches (and pass async callback to ipcMain of course).

The structure in a more simple form would look like the followings

function doJob () {
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      console.log('RETURN')
      resolve()
    }, 2000)
  })
}
// ipcMain.on('play', async (event, payload) => { ... })
(async () => {
  if (true) {
    await doJob()
  }
  if (true) {
    await doJob()
  }
})()


回答2:

I can see you are affecting launcherDir in all three times using it in the !if will always execute the first process even if it fails i.e the last if will be checking launcherDir + "\natives" + "\bin" or is this the desired behaviour if not i think all your if's will fail