Skip to content

Commit 2b601e0

Browse files
authored
Merge pull request #195 from rikkuness/getPortsPromises
Change getPorts to resolve all responses via promises to resolve race condition
2 parents 7c37cf6 + 0014929 commit 2b601e0

File tree

1 file changed

+57
-44
lines changed

1 file changed

+57
-44
lines changed

core/serial.js

Lines changed: 57 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -546,54 +546,67 @@ To add a new serial device, you must add an object to
546546
return oldListener;
547547
};
548548

549-
/* Calls 'callback(port_list, shouldCallAgain)'
550-
'shouldCallAgain==true' means that more devices
551-
may appear later on (eg Bluetooth LE).*/
552-
var getPorts=function(callback) {
553-
var ports = [];
554-
var newPortToDevice = [];
555-
// get all devices
556-
var responses = 0;
549+
/**
550+
* List ports available over all configured devices.
551+
* `shouldCallAgain` mean that more devices may appear later on (eg. Bluetooth LE)
552+
* @param {(ports, shouldCallAgain) => void} callback
553+
*/
554+
var getPorts = function (callback) {
555+
var newPortToDevice = {};
556+
557557
var devices = Espruino.Core.Serial.devices;
558-
if (!devices || devices.length==0) {
559-
portToDevice = newPortToDevice;
558+
if (!devices || devices.length == 0) {
559+
portToDevice = newPortToDevice;
560560
return callback(ports, false);
561561
}
562-
var shouldCallAgain = false;
563-
devices.forEach(function (device) {
564-
//console.log("getPorts -->",device.name);
565-
device.getPorts(function(devicePorts, instantPorts) {
566-
//console.log("getPorts <--",device.name);
567-
if (instantPorts===false) shouldCallAgain = true;
568-
if (devicePorts) {
569-
devicePorts.forEach(function(port) {
570-
var ignored = false;
571-
if (Espruino.Config.SERIAL_IGNORE)
572-
Espruino.Config.SERIAL_IGNORE.split("|").forEach(function(wildcard) {
573-
var regexp = "^"+wildcard.replace(/\./g,"\\.").replace(/\*/g,".*")+"$";
574-
if (port.path.match(new RegExp(regexp)))
575-
ignored = true;
576-
});
577562

578-
if (!ignored) {
579-
if (port.usb && port.usb[0]==0x0483 && port.usb[1]==0x5740)
580-
port.description = "Espruino board";
581-
ports.push(port);
582-
newPortToDevice[port.path] = device;
583-
}
584-
});
585-
}
586-
responses++;
587-
if (responses == devices.length) {
588-
portToDevice = newPortToDevice;
589-
ports.sort(function(a,b) {
563+
// Test to see if a given port path is ignore or not by configuration
564+
function isIgnored(path) {
565+
if (!Espruino.Config.SERIAL_IGNORE) return false;
566+
567+
return Espruino.Config.SERIAL_IGNORE.split("|").some((wildcard) => {
568+
const regexp = new RegExp(
569+
`^${wildcard.replace(/\./g, "\\.").replace(/\*/g, ".*")}$`
570+
);
571+
572+
return path.match(regexp);
573+
});
574+
}
575+
576+
// Asynchronously call 'getPorts' on all devices and map results back as a series of promises
577+
Promise.all(
578+
devices.map((device) =>
579+
new Promise((resolve) => device.getPorts(resolve)).then(
580+
(devicePorts, instantPorts) => ({
581+
device: device,
582+
shouldCallAgain: !instantPorts, // If the ports are not present now (eg. BLE) then call again
583+
value: (devicePorts || [])
584+
.filter((port) => !isIgnored(port.path)) // Filter out all the ignored ports
585+
.map((port) => {
586+
// Map a description for this particular Product/Vendor
587+
if (port.usb && port.usb[0] == 0x0483 && port.usb[1] == 0x5740)
588+
port.description = "Espruino board";
589+
return port;
590+
}),
591+
})
592+
)
593+
)
594+
).then((results) => {
595+
portToDevice = results.reduce((acc, promise) => {
596+
promise.value.forEach((port) => (acc[port.path] = promise.device));
597+
return acc;
598+
}, {});
599+
600+
callback(
601+
results
602+
.flatMap((result) => result.value)
603+
.sort((a, b) => {
590604
if (a.unimportant && !b.unimportant) return 1;
591605
if (b.unimportant && !a.unimportant) return -1;
592606
return 0;
593-
});
594-
callback(ports, shouldCallAgain);
595-
}
596-
});
607+
}),
608+
results.some((result) => result.shouldCallAgain),
609+
);
597610
});
598611
};
599612

@@ -604,10 +617,10 @@ To add a new serial device, you must add an object to
604617

605618
var openSerialInternal=function(serialPort, connectCallback, disconnectCallback, attempts) {
606619
/* If openSerial is called, we need to have called getPorts first
607-
in order to figure out which one of the serial_ implementations
608-
we must call into. */
620+
in order to figure out which one of the serial_ implementations
621+
we must call into. */
609622
if (portToDevice === undefined) {
610-
portToDevice = []; // stop recursive calls if something errors
623+
portToDevice = {}; // stop recursive calls if something errors
611624
return getPorts(function() {
612625
openSerialInternal(serialPort, connectCallback, disconnectCallback, attempts);
613626
});

0 commit comments

Comments
 (0)