Реализовать async/await в функции сортировки массивов javascript

Я пытаюсь реализовать метод сортировки на транспортире ElementArrayFinder, Как известно, все методы транспортирования возвращают обещания. Так что мой метод сортировки имеет условие, которое зависит от разрешения обещаний. Я использую плагин для узла async/await чтобы сделать его совместимым с версиями node.js ниже 6. (здесь плагин: https://www.npmjs.com/package/asyncawait)

Вот мой код, где this это ArrayElementFinder:

var asyncCompare = async(function(a, b) {
    let x = await (a.getText());
    let y = await (b.getText());
    console.log(x.localeCompare(y));
    return x.localeCompare(y);
});

var sortTheArray = async(function(arrayOfElementFinders) {
    return await (arrayOfElementFinders.sort(asyncCompare));
});

this.then((elements) => {
    let arrayOfElementFinders = [elements[0], elements[1], elements[2]];
    let sortedArray = sortTheArray(arrayOfElementFinders);
    console.log('array sorted');
});

К сожалению, время выполнения не то, что я ожидаю. Печать: array sorted происходит раньше, чем отпечатки сравнения x.localeCompare(y), Есть идеи, что я делаю не так? И есть идеи, как достичь моей цели?

Большое спасибо за любую помощь

3 ответа

Решение

sort не принимает асинхронный обратный вызов. Он ожидает числовое значение в качестве возвращаемого значения, а не обещание для него; и он возвращает массив, а не обещание. Обойти это невозможно (хотя можно реализовать собственный алгоритм асинхронной сортировки - даже параллельный).

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

Ты должен сделать

const elements = await this;
const arrayOfElementFinders = elements.slice(0, 3); // or Array.from?
const comparableArray = await Promise.all(arrayOfElementFinders.map(async x => [await x.getText(), x]));
comparableArray.sort((a, b) => +(a[0] > b[0]) || -(a[0] < b[0]));
const sortedArray = comparableArray.map(x => x[1]);
console.log('array sorted');

Разве он не поддерживает обещания до V6?

Я не люблю смешивать императив await с функционалом promise абстракция. Не могли бы вы сделать как?

Promise.all([Promise.resolve(10), Promise.resolve(4), Promise.resolve(7)])
       .then(r => r.sort((a,b) => a-b))
       .then(s => console.log(s));

и брызги .catch() этапы по мере необходимости?

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

Эта функция может выглядеть примерно так:

      type Box<T> = {  value: T };

const sortAsync = async <T>(
    arr: T[],
    cmp: (a: T, b: T) => Promise<number>
): Promise<T[]> => {

    // Keep a list of two values and the precedence
    const results: [Box<T>, Box<T>, number][] = [];

    // Box the value in case you're sorting primitive values and some
    // values occur more than once.
    const result: Box<T>[] = arr.map(value => ({ value }));

    for (; ;) {
        let nextA: Box<T>, nextB: Box<T>, requiresSample = false;

        result.sort((a, b) => {

            // Check if we have to get the precedence of a value anyways,
            // so we can skip this one.
            if (requiresSample) return 0;

            // See if we already now which item should take precedence
            const match = results.find(v => v[0] === a && v[1] === b);
            if (match) return match[2];

            // We need the precedence of these two elements
            nextA = a;
            nextB = b;
            requiresSample = true;
            return 0;
        });

        if (requiresSample) {
            // Let the async function calculate the next value
            results.push([nextA, nextB, await cmp(nextA.value, nextB.value)]);
        } else break; // It's fully sorted
    }

    // Map the sorted boxed-value array to its containing value back again
    return result.map(v => v.value);
};

Для простоты (и неизменности) я не изменяю исходный массив и просто возвращаю новый:

      
// Create new array and shuffle the values in it
const values = new Array(20)
    .fill(0)
    .map((_, index) => index)
    .sort(() => Math.random() > 0.5 ? 1 : -1);

console.log('Start: ', values);

const sorted = await sortAsync(values, async (a, b) => {
    // Simulate an async task by waiting some time
    await new Promise(resolve => setTimeout(resolve, Math.random() * 25));
    return a === b ? 0 : a > b ? 1 : -1;
});

console.log('End: ', sorted);
Другие вопросы по тегам