Turning nested jQuery Promises into flat code structure using .done() and .then()
You can substitute returning Promise objects from functions for using callbacks as parameters to function, to chain .then() and .catch(). Array.prototype.map() and Promise.all() can be substituted for jQuery.each() which does not await the fulfillment of Promise objects iterated.

Which requires returning the jQuery promise object from storeGroupOnServer

storeGroupOnServer = function(uuid, callback) {
    return $.post('saveGroup.php', {uuid:uuid},'json')
           .then(function(data) {
             return data.groupid;
           });
};


and returning a jQuery promise object of Promise from createGroupDomNode

createGroupDomNode = function(groupid, callback) {
    var cloner = $('#cloner');
    var newnode = cloner.clone(true);
    newnode.attr('id',groupid);
    newnode.removeClass('hide');
    cloner.after(newnode);
    return $.Deferred().resolve(newnode);
}


and awaiting &lt;img&gt; element load event 

function createNewChildDomNode(imgpath, groupdom){
    return $.Deferred(function(dfd) {
             imgnode = $('&lt;img/&gt;')
                       .on("load", dfd.resolve)
                       .on("error", dfd.reject)
                       .attr('src',imgpath);
             picnode = $('&lt;picture/&gt;').append(imgnode);
             $(groupdom).first().prepend(picnode);
           }).promise();
}


For example



const data = {
  uuid: {
    1: 4,
    2: 5,
    3: 6
  }
};
const response = {
  4: "a",
  5: "b",
  6: "c"
};

const groupPromise = Promise.resolve(data.uuid[1]); // initial `Promise`
const arrayOfFiles = [Promise.resolve({
  fileInfo: {
    cdnUrl: 10
  }
}), Promise.resolve({
  fileInfo: {
    cdnUrl: 20
  }
}), Promise.resolve({
  fileInfo: {
    cdnUrl: 30
  }
})];

const storeGroupOnServer = (uuid) =&gt; {
  console.log({
    uuid
  });
  // `return` response `Promise`
  return Promise.resolve(response[uuid])
};

const createGroupDomNode = (groupid) =&gt; {
  console.log({
    groupid
  });
  // `return` `groupid`
  return Promise.resolve(groupid)
};
const createNewChildDomNode = (groupdom, {
  // destructure `cdnUrl` define as `imgpath`
  fileInfo: {
    cdnUrl: imgpath 
  }
}) =&gt; {
  console.log({
    imgpath,
    groupdom
  });
  // process `&lt;img&gt;` elements here
  // `return` resolved or rejected `Promise` 
  // at `load` or `error` event
  return Promise.resolve("done processing " + imgpath)
};
let handleFiles = (groupnode, file) =&gt; {
  console.log({
    groupnode,
    file
  });
  // handle each `file` `Promise`, pass `group` node to 
  // function passed to `.then()`
  return file.then(createNewChildDomNode.bind(null, groupnode))
};
const handleGroupDomNodes = (groupnode) =&gt; {
  console.log({
    groupnode
  });
  // use `Promise.all()`, `.map()` to process array of `Promise` objects
  return Promise.all(
    arrayOfFiles.map(handleFiles.bind(null, groupnode))
  )
};

groupPromise
.then(storeGroupOnServer)
.then(createGroupDomNode)
.then(handleGroupDomNodes)
.then(complete =&gt; console.log(complete)) // to illustrate chain completes
.catch(err =&gt; console.error(err)); // handle errors in chain