jQuery .when () .then () обещают не придерживаться цикла for - PullRequest
1 голос
/ 07 марта 2020

Я перебираю базовый c JSON Объект с for l oop. JSON выглядит следующим образом:

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

Проблема, с которой я сталкиваюсь, заключается в цикле. Я пытаюсь l oop, используя .when() и .then() - ajax() вызывает go синхронно, как и ожидалось, но 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 l oop, он будет "удерживать его" до return доволен. Я просто не понимаю почему я не прав, и каково решение! Как правильно передать project в prepareLayer(project) синхронно с остальной частью сценария? Я знаю, что мои логики c несовершенны, я просто не вижу леса сквозь деревья.

Для справки при объяснении результата, вот как выглядит console.log() - Где область в синий - это то, что происходит мгновенно, а остальное происходит с def.then(function () {

CONSOLE LOG

Ответы [ 2 ]

2 голосов
/ 07 марта 2020

Вы, вероятно, хотите вместо этого работать с 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 голос
/ 09 марта 2020

Вот три способа сделать это с различными уровнями распараллеливания.

Чтобы загружать по одному и обрабатывать по одному:

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)

Boilerplate:

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 }) })
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...