Σύνθετες Υποσχέσεις στο Typescript/Javascript;
Γράφω μια κινητή εφαρμογή βασισμένη σε Angular2, χρησιμοποιώντας Typescript με το Nativescript runtime, αλλά αντιμετωπίζω κάποια προβλήματα με τα Promises. Έχω ένα HomeComponent, από το οποίο θα ήθελα να μπορώ να καλώ διάφορες λειτουργίες Bluetooth. Αυτές απαιτούν ανθρώπινη αλληλεπίδραση (όπως η επιλογή της συσκευής) και χρονικά διαστήματα αναμονής για την ανίχνευση Bluetooth, οπότε χρειάζονται αρκετά δευτερόλεπτα για να ολοκληρωθούν.
Θέλω να αφαιρέσω αυτές τις μεθόδους ως υποσχέσεις, οπότε έκανα το εξής:
HomeComponent.ts:
bluetoothAdd() {
this.isScanning = true;
this._ble.scan().then(
// Log the fulfillment value
function (val) {
this.isScanning = false; //hide activity indicator
this.Connect(this.ble._peripheral);
})
.catch(
function (reason) {
this.isScanning = false; //hide activity indicator
console.log('Handle rejected promise (' + reason + ') here.');
});
}
BluetoothUtils.ts (αυτό εισάγεται παραπάνω ως this._ble):
var devices: Array<string>;
var _peripheral: any;
export function scan() {
return new Promise((resolve, reject) => {
bluetooth.hasCoarseLocationPermission().then(
(granted) => {
if (!granted) {
bluetooth.requestCoarseLocationPermission();
} else {
bluetooth.startScanning({
serviceUUIDs: ["133d"],
seconds: 4,
onDiscovered: (peripheral) => {
console.log("Periperhal found with UUID: " + peripheral.UUID);
}
}).then(() => {
console.log("scanning complete");
}, (err) => {
console.log("error while scanning: " + err);
});
}
});
});
}
Αφού περιηγήθηκα στην εφαρμογή μου με τον αποσφαλματωτή, όταν καλείται η bluetoothAdd() στο HomeComponent μου, η συνάρτηση scan() στο BluetoothUtils λειτουργεί όπως αναμένεται και σε ένα σενάριο, εκτελεί τη γραμμή console.log("error while scanning: " + err);, λέγοντας:
σφάλμα κατά τη σάρωση: το Bluetooth δεν είναι ενεργοποιημένο
Ωστόσο, ούτε τα μέρη then ούτε τα catch του HomeComponent this._ble.scan() promise εκτελούνται; Γιατί συμβαίνει αυτό; Μπορείς να συνδυάσεις Promises (δηλαδή ένα promise μέσα σε ένα promise) όπως προσπαθώ να κάνω; ή κάποιες ιδέες για το πώς να το αντιμετωπίσω περαιτέρω;
Απάντηση #1
Υπάρχουν μερικά προβλήματα.
1/ Χρησιμοποιείς ένα αντι-παράδειγμα. Δεν υπάρχει λόγος να δημιουργήσεις μια νέα υπόσχεση στο scan αφού όλα όσα διαχειρίζεσαι είναι υποσχέσεις.
2/α) Εάν θέλετε να χρησιμοποιήσετε το αντι-μοτίβο, πρέπει να καλέσετε τις συναρτήσεις resolve και reject, δηλαδή στην τρέχουσα κατάσταση η νέα υπόσχεση δεν επιλύεται ούτε απορρίπτεται ποτέ, και συνεπώς η "κύρια" υπόσχεση στο scan δεν επιλύεται ούτε απορρίπτεται ποτέ.
2/β) Μια καλύτερη λύση: απλώς επιστρέψτε τις υποσχέσεις που έχουν ήδη δημιουργηθεί. Αν δεν τις επιστρέψετε, θα γίνουν ξεχωριστοί κλάδοι υποσχέσεων και δεν θα επηρεάσουν τον «κύριο» κλάδο υποσχέσεων στο scan, δηλαδή:
export function scan() {
return bluetooth.hasCoarseLocationPermission().then(
(granted) => {
if (!granted) {
return bluetooth.requestCoarseLocationPermission();
} else {
return bluetooth.startScanning({
serviceUUIDs: ["133d"],
seconds: 4,
onDiscovered: (peripheral) => {
console.log("Periperhal found with UUID: " + peripheral.UUID);
}
}).then(() => {
console.log("scanning complete");
}, (err) => {
console.log("error while scanning: " + err);
});
}
});
}
Σημειώστε τα 3 πρόσθετα return: ένα στην κορυφή του scan και στη συνέχεια πριν από τα bluetooth.requestCoarseLocationPermission και bluetooth.startScanning
Αν θέλεις να μάθεις ποια άλλα αντιαπαλλακτικά υποσχέσεων υπάρχουν, εδώ είναι μια υπέροχη πηγή
Απάντηση #2
Δεδομένου ότι χρησιμοποιείτε TypeScript, παρακαλώ μην περιμένετε πια με await/async. Είναι πολύ πιο σαφές, ειδικά όταν έχετε πολλαπλές υποσχέσεις.
async function bluetoothAdd() {
this.isScanning = true;
try {
const val = await this._ble.scan();
this.Connect(this.ble._peripheral);
} catch (reason) {
console.log('Handle rejected promise (' + reason + ') here.');
}finally{
this.isScanning = false;
}
}
//Bluetoo.....ts
var devices: Array<string>;
var _peripheral: any;
export async function scan() {
const granted = await bluetooth.hasCoarseLocationPermission();
if(!granted){
return bluetooth.requestCoarseLocationPermission();
}else{
try {
const val = await bluetooth.startScanning({
serviceUUIDs: ["133d"],
seconds: 4,
onDiscovered: (peripheral) => {
console.log("Periperhal found with UUID: " + peripheral.UUID);
}
});
console.log("scanning complete");
return val;
} catch (err) {
// you can rethrow err here such that user `bluetoothAdd` will recieve it/
console.log("error while scanning: " + err);
}
}
}
