typescript - Angular vänta på att en loop med löften ska köras innan nästa kodblock fortsätter
Jag försöker skicka data till en API-endpoint men av någon konstig anledning, som jag inte kan lista ut, skickas inte datan.
Nedan är metoden men när du granskar begäran.
syncPatients(){
let patients = [];
this.sqlite.create({name: 'criticare.db', location: 'default'})
.then((db: SQLiteObject) =>{
db.executeSql('SELECT * FROM patients where updated_at >= (?)',[this.settings.last_sync_time])
.then((data) =>{
if(data.rows.length > 0){
//let patients = [];
for(var i =0; i < data.rows.length; i++){
var _patient = data.rows.item(i);
//console.log(_patient);
db.executeSql('SELECT * FROM wards WHERE id = (?)',[_patient.id])
.then((ward_data)=>{
if(ward_data.rows.length > 0){
_patient.ward_id = ward_data.rows.item(0).online_ref;
patients.push(JSON.stringify(_patient));
//console.log(patients);
}else{
console.log("Ward not found");
}
})
}//end of for loop
}else{
console.log('No patients to sync');
}
})
.then(()=>{
console.log(patients);
var headers = new Headers();
headers.append("Accept", 'application/json');
headers.append('Content-Type', 'application/json; charset=utf-8');
headers.append('Authorization','Bearer '+ this.settings.token);
let options = new RequestOptions({ headers: headers });
let postParams = { patients: JSON.stringify(patients) };
console.log(patients);
this.http.post(this.api_url+"/patients/sync", postParams, options).subscribe(data => {
let response = JSON.parse(data['_body']);
if(response.status == "Ok"){
response.result.forEach(element =>{
db.executeSql('UPDATE patients SET online_ref = (?), updated_at = (?) WHERE id = (?)',[element.online_id, this.getCurrentTime(), element.id]).then(() => {
console.log('Updated patient ' + element.id)
}).catch(e =>{
console.log(e);
})
});
}else{
console.log("Error syncing patients");
}
}) //end of post
})
})
}
Problemet är i den sista .then(). I konsollloggarna före och efter jag gör let postParams={ patients: JSON.stringify(patients) }; visas att patients har rätt data, men när du inspekterar förfrågan är nyttolasten
{
"patients": ""
}
Vad missar jag eller gör fel?
Uppdatering Visade sig som @orodan och @jc-ford föreslog, att problemet var med att http-postingen kördes innan några löften löstes. Jag har omstrukturerat koden med deras förslag, infört async och await nedan och det fungerar utmärkt nu.
syncPatients(){
let patients = [];
this.sqlite.create({name: 'criticare.db', location: 'default'})
.then((db:SQLiteObject) => {
db.executeSql('SELECT * FROM patients where updated_at >= (?)',[this.settings.last_sync_time])
.then((data) =>{
if(data.rows.length > 0){
for(var i =0; i < data.rows.length; i++){
patients.push(data.rows.item(i));
}
}
else{
console.log('No patients to sync');
}
return patients;
})
.then(async (patients)=>{
const patientsPromises = patients.map(async (patient) =>{
const data = await db.executeSql('SELECT * FROM wards WHERE id = (?)',[patient.id])
patient.ward_id = data.rows.item(0).online_ref
})
// new patients
let all_promises = await Promise.all(patientsPromises);
console.log(patients);
var headers = new Headers();
headers.append("Accept", 'application/json');
headers.append('Content-Type', 'application/json; charset=utf-8');
headers.append('Authorization','Bearer '+ this.settings.token);
let options = new RequestOptions({ headers: headers });
let postParams = { patients: JSON.stringify(patients) };
this.http.post(this.api_url+"/patients/sync", postParams, options)
.subscribe(data => {
let response = JSON.parse(data['_body']);
if(response.status == "Ok"){
this.sqlite.create({name: 'criticare.db', location: 'default'})
.then((db:SQLiteObject) =>{
response.result.forEach(element =>{
db.executeSql('UPDATE patients SET online_ref = (?), updated_at = (?) WHERE id = (?)',[element.online_id, this.getCurrentTime(), element.id])
.then(() => {
console.log('Updated patient ' + element.id)
})
.catch(e =>{
console.log(e);
})
});
})
}else{
console.log("Error syncing patients");
}
}) //end of subscribe
})
})
}
Svar #1
Du har ett asynkront problem här: du väntar inte på att dina vårdinnehavares data ska vara här innan du gör ditt HTTP-anrop.
Eftersom du måste vänta på flera anrop till din databas innan du gör ditt HTTP-anrop, skulle jag råda att använda Promise.all vilket tillåter dig att vänta på att alla löften som skickats som en array-parameter ska slutföras innan du fortsätter med något. Det skulle se ut ungefär så här:
const dbCalls = [];
for (let i = 0; i < data.rows.length; i++) {
const _patient = data.rows.item(i);
dbCall = db.executeSql('SELECT * FROM wards WHERE id = (?)',[_patient.id]);
}
Promise.all(dbCalls)
.then(dbResults => {
dbResults.forEach(ward_data => {
if (ward_data.rows.length > 0) {
_patient.ward_id = ward_data.rows.item(0).online_ref;
patients.push(JSON.stringify(_patient));
} else {
console.log("Ward not found");
}
});
console.log(patients);
var headers = new Headers();
headers.append("Accept", 'application/json');
headers.append('Content-Type', 'application/json; charset=utf-8');
headers.append('Authorization','Bearer '+ this.settings.token);
let options = new RequestOptions({ headers: headers });
let postParams = { patients: JSON.stringify(patients) };
console.log(patients);
this.http.post(this.api_url+"/patients/sync", postParams, options).subscribe(data => {
let response = JSON.parse(data['_body']);
if(response.status == "Ok"){
response.result.forEach(element =>{
db.executeSql('UPDATE patients SET online_ref = (?), updated_at = (?) WHERE id = (?)',[element.online_id, this.getCurrentTime(), element.id]).then(() => {
console.log('Updated patient ' + element.id)
}).catch(e =>{
console.log(e);
})
});
}else{
console.log("Error syncing patients");
}
}) //end of post
})
Och angående varför du såg dina patienter medan du felsökte: lita inte på console.log. Eftersom du hade ett asynkront problem, vid någon tidpunkt, avslutades dina databas-anrop och dina patienter blev fyllda. console.log gav dig den "senaste versionen" av dina patienter, men vid det ögonblick du försökte göra ditt http-anrop, var de ännu inte här ^^
PS: Jag använde lite ES6-syntax i mitt exempel, om du inte kan, byt bara ut pilfunktioner mot "function" och alla const och let mot "var".
Svar #2
Jag tror problemet ligger i dina löften. Se mina kommentarer nedan.
syncPatients(){
let patients = [];
this.sqlite.create({name: 'criticare.db', location: 'default'})
.then((db: SQLiteObject) =>{
db.executeSql('SELECT * FROM patients where updated_at >= (?)',[this.settings.last_sync_time])
.then((data) =>{
if(data.rows.length > 0){
//let patients = [];
for(var i =0; i < data.rows.length; i++){
var _patient = data.rows.item(i);
//console.log(_patient);
//You're kicking off an asynchronous process here, but not chaining
//its promise to the current promise.
db.executeSql('SELECT * FROM wards WHERE id = (?)',[_patient.id])
.then((ward_data)=>{
if(ward_data.rows.length > 0){
_patient.ward_id = ward_data.rows.item(0).online_ref;
patients.push(JSON.stringify(_patient));
//console.log(patients);
}else{
console.log("Ward not found");
}
})
}//end of for loop
}else{
console.log('No patients to sync');
}
})
.then(()=>{
//This part executes before the executeSql() promises above return
//so the patients.push() above hasn't happened yet.
console.log(patients);
var headers = new Headers();
headers.append("Accept", 'application/json');
headers.append('Content-Type', 'application/json; charset=utf-8');
headers.append('Authorization','Bearer '+ this.settings.token);
let options = new RequestOptions({ headers: headers });
let postParams = { patients: JSON.stringify(patients) };
console.log(patients);
this.http.post(this.api_url+"/patients/sync", postParams, options).subscribe(data => {
let response = JSON.parse(data['_body']);
if(response.status == "Ok"){
response.result.forEach(element =>{
db.executeSql('UPDATE patients SET online_ref = (?), updated_at = (?) WHERE id = (?)',[element.online_id, this.getCurrentTime(), element.id]).then(() => {
console.log('Updated patient ' + element.id)
}).catch(e =>{
console.log(e);
})
});
}else{
console.log("Error syncing patients");
}
}) //end of post
})
})
}
Försök att returnera executeSql()-löften inneslutna i $q.all() (eller jag tror Promise.all() om du är i Angular 2+-världen) från ditt första .then() för att kedja ihop löftena. Jag tror de fungerar i stort sett på samma sätt, men här är länkar till dokumentationen för var och en.
https://docs.angularjs.org/api/ng/service/$q#all
https://developer.mozilla.org/sv/docs/Web/JavaScript/Reference/Global_Objects/Promise/all
