serge
serge

Reputation: 15239

Load AJAX collections in order in JavaScript

How to ensure, in JavaScript (jquery) that some actions are performed one after other, in an order.

Say, I need to load schools collection BEFORE loading teachers, in order to assing the myTeacher.SchoolName = schools[myTeacher.SchoolId].name;

The pseudo code bellow:

const studentsUrl='api/students', teachersUrl='api/teachers', schoolsUrl='api/schools';
let students = null, teachers = null, schools = null;

$(document).ready(function () {
    getSchools(); 
    getTeachers(); 
    getStudents(); 
});

function getSchools() {
    $.get(schoolsUrl, function (data) {
            window.schools = data;
        });
}

function getTeachers() {
    $.get(teachersUrl, function (data) {
            window.teachers = data;

            // >>> SHOULD BE SURE, SCHOOLS already loaded!!!
            $.each(teachers, function (key, item) {
                item.school = schools[item.schoolId].name;
            });
        });
}

function getStudents() {
    $.get(studentsUrl, function (data) {
            window.students = data;

            // >>> SHOULD BE SURE, TEACEHRS already loaded!!!
            $.each(students, function (key, item) {
                item.teacher = teachers[item.teacherId].name;
            });
        });
}

PS.

Is there another way to assure order but the encapsulation of one function at the end of another?

Upvotes: 0

Views: 62

Answers (6)

Jeto
Jeto

Reputation: 14927

If you want them in order (though I'm not sure I understand why, since you retrieve all schools/teachers/students anyway), you can simply do this.

Note: get* functions are dummies in the following sample. Instead, just return the result of $.get calls from them:

function getSchools() {
  return Promise.resolve({1: {name: 'school1'}});
}

function getTeachers() {
  return Promise.resolve({1: {name: 'teacher1', schoolId: 1}});
}

function getStudents() {
  return Promise.resolve({1: {name: 'student1', teacherId: 1}});
}

(async () => {
  const schools = await getSchools();
  const teachers = await getTeachers();
  const students = await getStudents();

    // Alternative for the $.each code
  Object.values(teachers).forEach(teacher => teacher.school = schools[teacher.schoolId].name);
  Object.values(students).forEach(student => student.teacher = teachers[student.teacherId].name);
  
  console.log(schools, teachers, students);
})();

Another note: this is ES8 code, I'll post a non async/await version if you need to support older browsers and can't use a transpiler like Babel.

Non ES8-dependent code:

function getSchools() {
  return Promise.resolve({1: {name: 'school1'}});
}

function getTeachers() {
  return Promise.resolve({1: {name: 'teacher1', schoolId: 1}});
}

function getStudents() {
  return Promise.resolve({1: {name: 'student1', teacherId: 1}});
}

let schools = null, teachers = null, students = null;

getSchools().then(_schools => {
  schools = _schools;
  return getTeachers();
}).then(_teachers => {
  teachers = _teachers;
  return getStudents();
}).then(_students => {
  students = _students;

  for (var _ in teachers) {
    teachers[_].school = schools[teachers[_].schoolId].name;
  }
  for (var _ in students) {
    students[_].teacher = teachers[students[_].teacherId].name
  }

  console.log(schools, teachers, students);
});

Upvotes: 1

Gabriele Petrioli
Gabriele Petrioli

Reputation: 196002

Since you only need all the results available and each request does not depend on the previous you can use jQuery.when

let students = null;
let teachers = null;
let schools = null;

$(document).ready(function() {
  $.when(
      getSchools(),
      getTeachers()
  ).done(function(shoolResults, teacherResults) {
      window.schools = shoolResults;
      window.teachers = teacherResults;
      handleTeachers();
      getStudents();
  });


  function getSchools() {
    return $.ajax({
      type: 'GET',
      url: schoolsUrl
    });
  }

  function getTeachers() {
    return $.ajax({
      type: 'GET',
      url: teachersUrl
    });
  }

  function handleTeachers() {
   $.each(teachers, function (key, item) { 
     item.school = schools[item.schoolId].name;
    });
  }
});

Upvotes: 1

Boris Evraev
Boris Evraev

Reputation: 82

As others already suggested you can chain requests. I made few changes to your code.

  1. Added Strict Mode it helps to prevent bugs
  2. The code wrapped in IFFE in order to prevent global pollution

If all apis belong to the same server you can process all this data on server side and return one filled json. in this way your server will do a little extra work on constructing this json but in other hand you will make only one ajax request instead of 3. This will work faster and you can cache this json for some time

Code for the first solution

(function () {

'use strict';
const studentsUrl = 'api/students';
const teachersUrl = 'api/teachers';
const schoolsUrl = 'api/schools';

let students = null;
let teachers = null;
let schools = null;

var scoolData = {
    schools: null,
    teachers: null,
    students: null
};

$(document).ready(function () {
    getSchools().then(function (schools) {
        scoolData.schools = schools;
        getTeachers().then(function (teachers) {
            scoolData.teachers = teachers;
            $.each(scoolData.teachers, function (key, item) {
                item.school = scoolData.schools[item.schoolId].name;
            });
        });
    });
});

function getSchools() {
    return $.get(schoolsUrl);
}

function getTeachers() {
    return $.get(teachersUrl,
        function (result) {
            scoolData.teachers = result;

            // >>> SHOULD BE SURE, SCHOOLS already loaded!!!
            $.each(teachers, function (key, item) {
                item.school = scoolData.schools[item.schoolId].name;
            });
        });
}
})();

Upvotes: 1

Hero Wanders
Hero Wanders

Reputation: 3292

You may load them asynchronously but you have to wait until both calls are finished.

To achieve this, add return before your ajax calls and combine the results in your ready function (not in the success handler of the teachers call):

let schoolsPromise = getSchools();
let teachersPromise = getTeachers();
$.when(schoolsPromise, teachersPromise)
  .then((schools, teachers) => {
    $.each(teachers, (key, item) => {
      item.school = schools[item.schoolId].name;
    });
  });

Upvotes: 0

Void Spirit
Void Spirit

Reputation: 909

I think you are looking for this one.

 getSchools().done(function(data){
      var someId = data.findThatId;
      getTeachers(someId);
 }); 

You will need to return data from ajax call to get data in done.

Upvotes: 0

DoobyScooby
DoobyScooby

Reputation: 377

Call getTeachers(); when getSchools(); return success or complete, success preferred since complete runs if there's an error..

Upvotes: 0

Related Questions