Here are the steps to take:
- Remove the - awaitkeyword where you currently have it. So now- subjectswill be an array of promises.
 
- Add - await Promise.allin your- pushcall:
 - terms.push({
    name: term._id,
    subjects: (await Promise.all(subjects)).flat(),
});
 
That's it: now there will multiple pending promises until one item is pushed to the terms array. This should already give some improvement.
If you want to have even more simultaneous pending promises, then also make terms an array of promises:
terms.push(Promise.all(subjects).then(subjects => ({
    name: term._id,
    subjects: subjects.flat(),
})));
And then, after the outermost loop, have a one-for-all await:
terms = await Promise.all(terms);
Demo
I created some mock data along the structure that your code expects, with a mock getData function, ...etc, so that code runs without errors. Here are the three versions of the code:
Original code
const delay = ms => new Promise(resolve => setTimeout(resolve, ms));
const app = {
    selectedGroup: {groups: ["A"],students: ["Student1", "Student2"]},
    terms: [{assessments: [{year_groups: [{name: "A"}]}, {year_groups: [{name: "E"}]}],_id: 123}, 
            {assessments: [{year_groups: [{name: "C"}]}],_id: 567}],
    dataService: {
        async getData(assessment, group, students) {
            await delay(100);
            return students.map(student => "++" + student);
        }
    },
    
    async f() { // The function in the question (no change)
        let terms = [];
        this.selectedGroup.groups = ['A', 'B', 'C', 'D', 'E', 'F'];
        for (let term of this.terms) {
          let subjects = [];
          //loop through each terms assessments
          for (let assessment of term.assessments) {
            //loop through the assessment year groups
            for (let assessmentGroup of assessment.year_groups) {
              //only get the assessments for the particular year group we are looking at
              if (this.selectedGroup.groups.includes(assessmentGroup.name)) {
                //get the subject data for this assessment
                let newSubjects =  await this.dataService.getData(
                  assessment,
                  assessmentGroup,
                  this.selectedGroup.students
                );
                //add the assessment subjects to the overall subjects array
                subjects = subjects.concat(newSubjects)
              }
            }
          }
          //push all the subjects for the term into a terms array
          terms.push({
            name: term._id,
            subjects: subjects,
          });
        }
        return terms;
    }
};
app.f().then(console.log);
 
 
With subjects being promises
const delay = ms => new Promise(resolve => setTimeout(resolve, ms));
const app = {
    selectedGroup: {groups: ["A"],students: ["Student1", "Student2"]},
    terms: [{assessments: [{year_groups: [{name: "A"}]}, {year_groups: [{name: "E"}]}],_id: 123}, 
            {assessments: [{year_groups: [{name: "C"}]}],_id: 567}],
    dataService: {
        async getData(assessment, group, students) {
            await delay(100);
            return students.map(student => "++" + student);
        }
    },
    
    async f() { // The function with first modification
        let terms = [];
        this.selectedGroup.groups = ['A', 'B', 'C', 'D', 'E', 'F'];
        for (let term of this.terms) {
          let subjects = [];
          //loop through each terms assessments
          for (let assessment of term.assessments) {
            //loop through the assessment year groups
            for (let assessmentGroup of assessment.year_groups) {
              //only get the assessments for the particular year group we are looking at
              if (this.selectedGroup.groups.includes(assessmentGroup.name)) {
                // Removed `await`
                let newSubjects = this.dataService.getData(
                  assessment,
                  assessmentGroup,
                  this.selectedGroup.students
                );
                //add the assessment subjects to the overall subjects array
                subjects = subjects.concat(newSubjects)
              }
            }
          }
          //Wait for all subjects promises to resolve
          terms.push({
            name: term._id,
            subjects: (await Promise.all(subjects)).flat(),
          });
        }
        return terms;
    }
};
app.f().then(console.log);
 
 
With subjects and terms being promises
const delay = ms => new Promise(resolve => setTimeout(resolve, ms));
const app = {
    selectedGroup: {groups: ["A"],students: ["Student1", "Student2"]},
    terms: [{assessments: [{year_groups: [{name: "A"}]}, {year_groups: [{name: "E"}]}],_id: 123}, 
            {assessments: [{year_groups: [{name: "C"}]}],_id: 567}],
    dataService: {
        async getData(assessment, group, students) {
            await delay(100);
            return students.map(student => "++" + student);
        }
    },
    
    async f() { // The function with second modification
        let terms = [];
        this.selectedGroup.groups = ['A', 'B', 'C', 'D', 'E', 'F'];
        for (let term of this.terms) {
          let subjects = [];
          //loop through each terms assessments
          for (let assessment of term.assessments) {
            //loop through the assessment year groups
            for (let assessmentGroup of assessment.year_groups) {
              //only get the assessments for the particular year group we are looking at
              if (this.selectedGroup.groups.includes(assessmentGroup.name)) {
                // Removed `await`
                let newSubjects = this.dataService.getData(
                  assessment,
                  assessmentGroup,
                  this.selectedGroup.students
                );
                //add the assessment subjects to the overall subjects array
                subjects = subjects.concat(newSubjects)
              }
            }
          }
          //push a promise for all subjects promises to resolve
          terms.push(Promise.all(subjects).then(subjects => ({
            name: term._id,
            subjects: subjects.flat(),
          })));
        }
        // One final spot to await all terms promises
        terms = await Promise.all(terms);
        return terms;
    }
};
app.f().then(console.log);
 
 
As you can see the output is the same for each version.
But all of them output duplicate data: I don't know what your getData does, but if it uses the students argument, like I did, for building the result, then surely the result will have duplicate students. Similar duplication can occur because of the assessment parameter, which also is repeated by the inner loop. As you said your code was correct, I leave that for you to further assess.