最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

sorting - Implement asyncawait in sort function of arrays javascript - Stack Overflow

programmeradmin3浏览0评论

I am trying to implement a sort method on protractor ElementArrayFinder. As known, all protractor methods return promises. So my sort method has a condition which depends on promises resolution. I am using a node plugin for async/await in order to make it compatible with node.js versions lower than 6. (Here the plugin: )

Here my code, where this is the 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');
});

Unfortunately the timing execution is not the one I expect. The print: array sorted happens before than the prints of comparing x.localeCompare(y). Any idea what am I doing wrong? And any idea how to achieve my objective?

Thanks a lot for any help

I am trying to implement a sort method on protractor ElementArrayFinder. As known, all protractor methods return promises. So my sort method has a condition which depends on promises resolution. I am using a node plugin for async/await in order to make it compatible with node.js versions lower than 6. (Here the plugin: https://www.npmjs.com/package/asyncawait)

Here my code, where this is the 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');
});

Unfortunately the timing execution is not the one I expect. The print: array sorted happens before than the prints of comparing x.localeCompare(y). Any idea what am I doing wrong? And any idea how to achieve my objective?

Thanks a lot for any help

Share Improve this question asked Aug 13, 2017 at 14:09 quirimmoquirimmo 9,9883 gold badges32 silver badges45 bronze badges 1
  • Array.prototype.sort is a synchronous function. – tibetty Commented Aug 13, 2017 at 14:17
Add a comment  | 

3 Answers 3

Reset to default 17

sort does not take an asynchronous callback. It expects a numeric value as the return value, not a promise for one; and it does return an array not a promise. There's no way around this (though one could implement a custom asynchronous sorting algorithm - even a parallel one).

But that's pretty inefficient anyway. Doing asynchronous fetching of compare values in every sort step will be slow - and it will fetch the same value per element multiple times. Don't do that. Instead, use a Schwartzian transform to fetch the values to sort by before, then sort (synchronously), then use the results.

You should do

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');

Does it not support promises before V6?

I dislike mixing imperative await with functional promise abstraction. Wouldn't you be able to do like?

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

ans splatter .catch() stages as needed?

I can see the use of this in cases where the sort function is user-facing and someone is, for example, being shown two pictures and has to pick one over another. In this case it's possible to just re-run the sort function over and over again until you have the precedence of all items the sort-function needs to fully sort your array.

This function could look something like this:

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

For simplicity (and the sake of immutability) I don't modify the original array and just return a new one:


// 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);
发布评论

评论列表(0)

  1. 暂无评论