jQuery .when() .then() promise 不遵守 for 循环

jQuery .when() .then() promise not adhering inside for loop

我正在使用 for 循环遍历基本 JSON 对象。 JSON 看起来像:

{
  "1": {
    "project_name": "Example Project Name 1",
    "client_name": "John Doe"
  },
  "2": {
    "project_name": "Example Project Name  2",
    "client_name": "John Doe"
  },
  /// -------
}

我遇到的问题是在遍历它时。我正在尝试使用 .when().then() 进行循环 - ajax() 调用按预期同步进行,但是 input 表示 ajax() 始终是 JSON.

的最后一个索引
function bulkUploadGo() {
    var def = $.when();

    for (var i = 1; i < Object.keys(projects_json).length + 1; i++) {
        var project = Object(projects_json)[i];

        // THIS WILL LOOP INSTANTLY -- SO I WILL SEE 1 - X INSTANTLY
        console.log(project); 

        def = def.then(function () {

             // THIS DISPLAYS SYNCHRONOUSLY, BUT IS ALWAYS SET TO THE LAST INDEX BECAUSE OF INSTANT LOOP ABOVE
            console.log(project); 

            return prepareLayer(project);
        });
    }
}

function prepareLayer(project) {
    return $.ajax({
        type: "POST",
        url: "/app/ajax/calls/process_project.php",
        dataType: 'html',
        data: {project: project}
    }).then(function (data) {
        var obj = JSON.parse(data); 
        // Do stuff
    });
}

显然我错误地认为因为 def = def.then(function () { 直接在 for 循环内所以它会 "hold it" 直到 return 被满足。我只是不明白为什么我错了,解决办法是什么!如何与脚本的其余部分同步正确地将 project 传递给 prepareLayer(project)?我知道我的逻辑有问题,就是见树不见林。

作为解释结果的参考,这里是 console.log() 的样子 -- 蓝色区域是立即发生的,其余的是 def.then(function () {

您可能希望使用 Promise.all,同时跟踪各个任务的进度。另请注意,现代浏览器丝毫不需要 jQuery,您所做的一切都已经有了纯 JS API:

const APIEndpoint = `/app/ajax/calls/process_project.php`;
const yourData = { ...... };
const dataKeys = Object.keys(yourData);
const progressBar = new IncrementalProgressBar(dataKeys.length);

/**
 * You're constantly posting to the same thing: let's make that a function.
 */
function postData(data = {}) {
  return fetch(APIEndpoint, {
    method: `POST`,
    headers: {
      "Content-Type": `application/json`
    },
    body: JSON.stringify(data)
  });
}

/**
 * Turn {yourData,key} into a promise around posting your data.
 */
function keyToPromise(key) {
  return new Promise((resolve, reject) => {
    const data = yourData[key];

    postData(data)
      .then(result => {
        progressBar.increment();
        resolve(result);
      })
      .catch(error => {
        progressBar.increment({error: `${key} failed`});
        reject(error);
      });
  };
}

// And now we just... run through all the things
Promise
  .all(dataKeys.map(keyToPromise)))
  .then(result => moveOnWhenDone())
  .catch(error => handleException());

以下是执行此操作的三种方法,具有不同的并行化级别。

要逐一上传,逐一处理:

const bulkUpload = async(arr,processResponse=identity)=>{
    for (let el in arr) {
        const response = await upload(el)
        await processResponse(response)
    }
}
bulkUpload(projects.values, processResponse).then(...)

并行上传并在我们收到所有回复后逐个处理:

const bulkUpload = async(arr)=>await Promise.allSettled(arr.map(upload))
bulkUpload(projects.values).then((allResponses) => /* process the responses */)

并行上传并处理每个响应:

const uploadAndProcess = (el) => upload(el).then(processResponse)
const bulkUpload = async(arr)=>await Promise.allSettled(arr.map(uploadAndProcess))
bulkUpload(projects.values)

样板文件:

const projects = { "1": { "project_name": "P1", "client_name": "C1" }, "2": { "project_name": "P2", "client_name": "C2" }, }
const identity = (x)=>x
cont URL = '/app/ajax/calls/process_project.php'
const upload = (item)=>fetch(URL, requestOptions(item))
const requestOptions = (project)=>({ method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ project }) })