Реализовать 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);