jQuery .when().then()承诺不会在循环中坚持

时间:2020-03-07 00:20:08

标签: javascript jquery .when

我正在使用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()调用按预期方式同步进行,但是所说的ajax() input 是始终是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循环内,它将“保留”直到满足return。我只是不明白为什么为什么错了,解决方案是什么!如何与脚本的其余部分同步地将project正确地传递到prepareLayer(project)中?我知道我的逻辑有缺陷,我只是看不见树林。

为了解释结果,以下是console.log()的样子-蓝色区域是立即发生的事,其余区域是def.then(function () {

CONSOLE LOG

2 个答案:

答案 0 :(得分:2)

您可能想代替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());

答案 1 :(得分:1)

这里有三种方法,并具有不同的并行化级别。

要一一上传并一一处理:

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 }) })