Можно ли использовать syn c обещание сна внутри jQuery .each ()? - PullRequest
4 голосов
/ 09 марта 2020

Так в принципе. Я хочу использовать jQuery каждую функцию внутри функции syn c. Однако, насколько я знаю. Это не работает, потому что каждая функция просто проверяет, является ли возвращаемое значение ложным, чтобы сломать l oop. Он не проверяет, является ли это обещанием, а затем заставляет его ждать его разрешения. Есть ли простой способ сделать каждую функцию jQuery синхронной с обещаниями?

В настоящее время у меня есть альтернатива, которая заключается в создании для l oop итерации элементов, но для его записи требуется немного больше времени поэтому я хотел бы избежать этого, если это возможно.

Вот скрипка: https://jsfiddle.net/3pcvqswn/2/

function sleep(ms)
{
    return new Promise(resolve => setTimeout(resolve, ms));
}

$(document).ready(function()
{
    (async () =>
  {
    console.log("This method works, but its lengthy to write");
    var elements = $("div");

    for(var index = 0; index < elements.length; index++)
    {
        var el = $(elements.eq(index));

        console.log("The content is: " + el.text());

      await sleep(1000);
        }

    console.log("This method doesn't work, but its easier to write");
    $("div").each(async (index, el) =>
    {
        console.log("The content is: " + $(el).text());

      // doesn't wait
      await sleep(1000);
    });
  })();
});

Я решил go с for of, но более точное решение для обратного вызова как индекса, так и элемента будет

  Object.entries($("div")).forEach(async ([index, el]) =>
  {
    console.log("row ", index, el);
  });

Ответы [ 3 ]

3 голосов
/ 09 марта 2020

Нет - jQuery .each работает аналогично forEach. Каждый обратный вызов будет запускаться один за другим, и если обратный вызов возвращает Promise (например, если это асиновая c функция), его не будут ждать - он работает аналогично

callback();
callback();
callback();

Если что-то в обратном вызове не блокирует (чего не следует делать, если вы используете приличные методы программирования), ничто из асинхронного инициализированного в обратном вызове не может привести к дальнейшим обратным вызовам, ожидающим завершения этого асинхронного действия.

Но jQuery объекты являются итеративными, поэтому тривиально использовать for..of для итерации по элементам просто:

const sleep = ms => new Promise(res => setTimeout(res, ms));

(async () => {
  for (const elm of $("div")) {
    console.log("The content is: " + $(elm).text());
    await sleep(1000);
  }
})();
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div>text 1</div>
<div>text 2</div>
<div>text 3</div>

Это совсем не многословно IMO.

Для использования await в al oop, l oop либо должен быть for l oop (либо for..in, for..of, либо for (let i = ...), либо итерационный механизм l oop должен быть построен так, чтобы он ожидает разрешения последнего обратного вызова, например, с reduce:

const sleep = ms => new Promise(res => setTimeout(res, ms));

[...$("div")].reduce(async (lastProm, elm) => {
  await lastProm;
  console.log("The content is: " + $(elm).text());
  await sleep(1000);
}, Promise.resolve());
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div>text 1</div>
<div>text 2</div>
<div>text 3</div>

Примечание, о котором едва ли стоит упоминать - ранее я говорил, что использование await внутри .each не будет работать

Если что-то в обратном вызове блокирует (чего не должно быть, если вы используете приличные методы программирования)

Если вы пишете дорогую и глупую блокирующую версию sleep, который потребляет все ресурсы, пока не будет достигнута следующая секунда, «можно» ждать внутри l oop, но этого не следует делать:

const sleep = ms => {
  const now = Date.now();
  while (Date.now() - now < ms) {}
}

Работать будет намного лучше с обещаниями правильно.

1 голос
/ 09 марта 2020

Вы не можете заставить $.each работать последовательно (один за другим). Он запускает функцию для каждого элемента «одновременно». Один из вариантов - написать собственную функцию each, которая просто выполняет более длинную версию, которую вы написали в своем вопросе:

const serialEach = async (f, elements) => {
   for(let index = 0; index < elements.length; index++) {
       const el = $(elements.eq(index))

       await f(el)
    }
}

Затем вы можете запустить ее так:

function sleep(ms) {
    return new Promise(resolve => setTimeout(resolve, ms))
}

$(document).ready(() => {
    const elements = $("div")

    serialEach(async (el) => {
        console.log("The content is: " + el.text())

        await sleep(1000)
    }, elements)
})

Примечание что serialEach возвращает обещание, поэтому оно будет немедленно возвращено, и любой код, который вы получите после вызова serialEach, будет выполнен, даже когда serialEach выполняет итерации по вашим элементам. Если вы хотите дождаться завершения этой итерации, прежде чем продолжить, вы можете просто await serialEach (и сделать $.ready обратный вызов async).

0 голосов
/ 09 марта 2020

Я бы не советовал использовать .each для этого. функция выполняется независимо от того, асинхронна она или нет. Но с программированием вы можете быть креативными.

//code

function sleep(ms) {
    return new Promise(resolve => setTimeout(resolve, ms));
}

let promise = Promise.resolve();
$("div").each((index, el) => {
  promise = promise.then(async () => {
    console.log("The content is: " + $(el).text());

    await sleep(1000);
  });
});

await promise;

//code
...