Catch leaked exceptions in superspawn and convert them to rejections

At least until Node.js 8, child_process.spawn will throw exceptions
instead of emitting error events in certain cases (like EACCES), Thus we
have to wrap the execution in try/catch to convert them into rejections.
diff --git a/spec/superspawn.spec.js b/spec/superspawn.spec.js
index 95056dc..9c05129 100644
--- a/spec/superspawn.spec.js
+++ b/spec/superspawn.spec.js
@@ -89,6 +89,25 @@
             });
     });
 
+    it('Test 005 : should not throw but reject', () => {
+        if (process.platform === 'win32') {
+            pending('Test should not run on Windows');
+        }
+
+        // Our non-executable (as in no execute permission) script
+        const TEST_SCRIPT = path.join(__dirname, 'fixtures/echo-args.cmd');
+
+        let promise;
+        expect(() => { promise = superspawn.spawn(TEST_SCRIPT, []); }).not.toThrow();
+
+        return Promise.resolve(promise).then(_ => {
+            fail('Expected promise to be rejected');
+        }, err => {
+            expect(err).toEqual(jasmine.any(Error));
+            expect(err.code).toBe('EACCES');
+        });
+    });
+
     describe('operation on windows', () => {
         const TEST_SCRIPT = path.join(__dirname, 'fixtures/echo-args.cmd');
         const TEST_ARGS = [ 'install', 'foo@^1.2.3', 'c o r d o v a' ];
diff --git a/src/superspawn.js b/src/superspawn.js
index 22daf88..2e58b2e 100644
--- a/src/superspawn.js
+++ b/src/superspawn.js
@@ -87,7 +87,15 @@
 
     events.emit(opts.printCommand ? 'log' : 'verbose', 'Running command: ' + cmd + ' ' + args.join(' '));
 
-    var child = crossSpawn.spawn(cmd, args, spawnOpts);
+    // At least until Node.js 8, child_process.spawn will throw exceptions
+    // instead of emitting error events in certain cases (like EACCES), Thus we
+    // have to wrap the execution in try/catch to convert them into rejections.
+    try {
+        var child = crossSpawn.spawn(cmd, args, spawnOpts);
+    } catch (e) {
+        whenDone(e);
+        return d.promise;
+    }
     var capturedOut = '';
     var capturedErr = '';
 
@@ -110,8 +118,10 @@
     child.on('close', whenDone);
     child.on('error', whenDone);
     function whenDone (arg) {
-        child.removeListener('close', whenDone);
-        child.removeListener('error', whenDone);
+        if (child) {
+            child.removeListener('close', whenDone);
+            child.removeListener('error', whenDone);
+        }
         var code = typeof arg === 'number' ? arg : arg && arg.code;
 
         events.emit('verbose', 'Command finished with error code ' + code + ': ' + cmd + ' ' + args);