使用返回promise的函数过滤数组

时间:2022-06-15 19:36:53

Given

特定

let arr = [1,2,3];

function filter(num) {
  return new Promise((res, rej) => {
    setTimeout(() => {
      if( num === 3 ) {
        res(num);
      } else {
        rej();
      }
    }, 1);
  });
 }

 function filterNums() {
   return Promise.all(arr.filter(filter));
 }

 filterNums().then(results => {
   let l = results.length;
   // length should be 1, but is 3
 });

The length is 3 because Promises are returned, not values. Is there a way to filter the array with a function that returns a Promise?

长度为3,因为返回Promises,而不是值。有没有办法用返回Promise的函数过滤数组?

Note: For this example, fs.stat has been replaced with setTimeout, see https://github.com/silenceisgolden/learn-esnext/blob/array-filter-async-function/tutorials/array-filter-with-async-function.js for the specific code.

注意:对于此示例,fs.stat已替换为setTimeout,请参阅https://github.com/silenceisgolden/learn-esnext/blob/array-filter-async-function/tutorials/array-filter-with-async- function.js用于特定代码。

6 个解决方案

#1


13  

As mentioned in the comments, Array.prototype.filter is synchronous and therefore does not support Promises.

正如评论中所提到的,Array.prototype.filter是同步的,因此不支持Promises。

Since you can now (theoretically) subclass built-in types with ES6, you should be able to add your own asynchronous method which wraps the existing filter function:

既然您现在(理论上)可以使用ES6子类化内置类型,那么您应该能够添加自己的异步方法来包装现有的过滤器函数:

Note: I've commented out the subclassing, because it's not supported by Babel just yet for Arrays

注意:我已经注释掉了子类,因为Babel还不支持Arrays

class AsyncArray /*extends Array*/ {
  constructor(arr) {
    this.data = arr; // In place of Array subclassing
  }

  filterAsync(predicate) {
     // Take a copy of the array, it might mutate by the time we've finished
    const data = Array.from(this.data);
    // Transform all the elements into an array of promises using the predicate
    // as the promise
    return Promise.all(data.map((element, index) => predicate(element, index, data)))
    // Use the result of the promises to call the underlying sync filter function
      .then(result => {
        return data.filter((element, index) => {
          return result[index];
        });
      });
  }
}
// Create an instance of your subclass instead
let arr = new AsyncArray([1,2,3,4,5]);
// Pass in your own predicate
arr.filterAsync(async (element) => {
  return new Promise(res => {
    setTimeout(() => {
      res(element > 3);
    }, 1);
  });
}).then(result => {
  console.log(result)
});

Babel REPL Demo

Babel REPL演示

#2


17  

Here's a way:

这是一种方式:

var wait = ms => new Promise(resolve => setTimeout(resolve, ms));
var filter = num => wait(1).then(() => num == 3);

var filterAsync = (array, filter) =>
  Promise.all(array.map(entry => filter(entry)))
  .then(bits => array.filter(entry => bits.shift()));

filterAsync([1,2,3], filter)
.then(results => console.log(results.length))
.catch(e => console.error(e));

The filterAsync function takes an array and a function that must either return true or false or return a promise that resolves to true or false, what you asked for (almost, I didn't overload promise rejection because I think that's a bad idea). Let me know if you have any questions about it.

filterAsync函数接受一个数组和一个必须返回true或false的函数,或者返回一个解析为true或false的promise,你要求的(几乎,我没有超载promise promise,因为我认为这是一个坏主意)。如果您对此有任何疑问,请与我们联系。

var wait = ms => new Promise(resolve => setTimeout(resolve, ms));
var filter = num => wait(1).then(() => num == 3);

var filterAsync = (array, filter) =>
  Promise.all(array.map(entry => filter(entry)))
  .then(bits => array.filter(entry => bits.shift()));

filterAsync([1,2,3], filter)
.then(results => console.log(results.length))
.catch(e => console.error(e));

var console = { log: msg => div.innerHTML += msg + "<br>",
                error: e => console.log(e +", "+ (e.lineNumber-25)) };
<div id="div"></div>

#3


13  

Here is a 2017 elegant solution using async/await :

这是使用async / await的2017优雅解决方案:

Very straightforward usage:

非常简单的用法:

const results = await filter(myArray, async num => {
  await doAsyncStuff()
  return num > 2
})

The helper function (copy this into your web page):

辅助函数(将其复制到您的网页中):

async function filter(arr, callback) {
  const fail = Symbol()
  return (await Promise.all(arr.map(async item => (await callback(item)) ? item : fail))).filter(i=>i!==fail)
}

Demo:

演示:

// Async IIFE
(async function() {
  const myArray = [1, 2, 3, 4, 5]

  // This is exactly what you'd expect to write 
  const results = await filter(myArray, async num => {
    await doAsyncStuff()
    return num > 2
  })

  console.log(results)
})()


// Arbitrary asynchronous function
function doAsyncStuff() {
  return Promise.resolve()
}


// The helper function
async function filter(arr, callback) {
  const fail = Symbol()
  return (await Promise.all(arr.map(async item => (await callback(item)) ? item : fail))).filter(i=>i!==fail)
}

I'll even throw in a CodePen.

我甚至会投入一个CodePen。

#4


8  

Promise Reducer to the rescue!

承诺减速机救援!

[1, 2, 3, 4].reduce((op, n) => {
    return op.then(filteredNs => {
        return new Promise(resolve => {
            setTimeout(() => {
                if (n >= 3) {
                    console.log("Keeping", n);
                    resolve(filteredNs.concat(n))
                } else {
                    console.log("Dropping", n);
                    resolve(filteredNs);
                }
            }, 1000);
        });
    });
}, Promise.resolve([]))
.then(filteredNs => console.log(filteredNs));

Reducers are awesome. "Reduce my problem to my goal" seems to be a pretty good strategy for anything more complex than what the simple tools will solve for you, i.e. filtering an array of things that aren't all available immediately.

减速器非常棒。 “将我的问题减少到我的目标”对于任何比简单工具为您解决的更复杂的事情来说似乎是一个非常好的策略,即过滤一系列并非全部可用的事物。

#5


0  

A valid way to do this (but it seems too messy):

这是一种有效的方法(但它似乎太乱了):

let arr = [1,2,3];

function filter(num) {
  return new Promise((res, rej) => {
    setTimeout(() => {
      if( num === 3 ) {
        res(num);
      } else {
        rej();
      }
    }, 1);
  });
}

async function check(num) {
  try {
    await filter(num);
    return true;
  } catch(err) {
    return false;
  }
}

(async function() {
  for( let num of arr ) {
    let res = await check(num);
    if(!res) {
      let index = arr.indexOf(num);
      arr.splice(index, 1);
    }
  }
})();

Again, seems way too messy.

再次,似乎太乱了。

#6


0  

Late to the game but since no one else mentioned it, Bluebird supports Promise.map which is my go-to for filters requiring aysnc processing for the condition,

在游戏的后期,但由于没有其他人提及它,Bluebird支持Promise.map,这是我需要aysnc处理条件的过滤器,

function filterAsync(arr) {
    return Promise.map(arr, num => {
        if (num === 3) return num;
    })
        .filter(num => num !== undefined)
}

#1


13  

As mentioned in the comments, Array.prototype.filter is synchronous and therefore does not support Promises.

正如评论中所提到的,Array.prototype.filter是同步的,因此不支持Promises。

Since you can now (theoretically) subclass built-in types with ES6, you should be able to add your own asynchronous method which wraps the existing filter function:

既然您现在(理论上)可以使用ES6子类化内置类型,那么您应该能够添加自己的异步方法来包装现有的过滤器函数:

Note: I've commented out the subclassing, because it's not supported by Babel just yet for Arrays

注意:我已经注释掉了子类,因为Babel还不支持Arrays

class AsyncArray /*extends Array*/ {
  constructor(arr) {
    this.data = arr; // In place of Array subclassing
  }

  filterAsync(predicate) {
     // Take a copy of the array, it might mutate by the time we've finished
    const data = Array.from(this.data);
    // Transform all the elements into an array of promises using the predicate
    // as the promise
    return Promise.all(data.map((element, index) => predicate(element, index, data)))
    // Use the result of the promises to call the underlying sync filter function
      .then(result => {
        return data.filter((element, index) => {
          return result[index];
        });
      });
  }
}
// Create an instance of your subclass instead
let arr = new AsyncArray([1,2,3,4,5]);
// Pass in your own predicate
arr.filterAsync(async (element) => {
  return new Promise(res => {
    setTimeout(() => {
      res(element > 3);
    }, 1);
  });
}).then(result => {
  console.log(result)
});

Babel REPL Demo

Babel REPL演示

#2


17  

Here's a way:

这是一种方式:

var wait = ms => new Promise(resolve => setTimeout(resolve, ms));
var filter = num => wait(1).then(() => num == 3);

var filterAsync = (array, filter) =>
  Promise.all(array.map(entry => filter(entry)))
  .then(bits => array.filter(entry => bits.shift()));

filterAsync([1,2,3], filter)
.then(results => console.log(results.length))
.catch(e => console.error(e));

The filterAsync function takes an array and a function that must either return true or false or return a promise that resolves to true or false, what you asked for (almost, I didn't overload promise rejection because I think that's a bad idea). Let me know if you have any questions about it.

filterAsync函数接受一个数组和一个必须返回true或false的函数,或者返回一个解析为true或false的promise,你要求的(几乎,我没有超载promise promise,因为我认为这是一个坏主意)。如果您对此有任何疑问,请与我们联系。

var wait = ms => new Promise(resolve => setTimeout(resolve, ms));
var filter = num => wait(1).then(() => num == 3);

var filterAsync = (array, filter) =>
  Promise.all(array.map(entry => filter(entry)))
  .then(bits => array.filter(entry => bits.shift()));

filterAsync([1,2,3], filter)
.then(results => console.log(results.length))
.catch(e => console.error(e));

var console = { log: msg => div.innerHTML += msg + "<br>",
                error: e => console.log(e +", "+ (e.lineNumber-25)) };
<div id="div"></div>

#3


13  

Here is a 2017 elegant solution using async/await :

这是使用async / await的2017优雅解决方案:

Very straightforward usage:

非常简单的用法:

const results = await filter(myArray, async num => {
  await doAsyncStuff()
  return num > 2
})

The helper function (copy this into your web page):

辅助函数(将其复制到您的网页中):

async function filter(arr, callback) {
  const fail = Symbol()
  return (await Promise.all(arr.map(async item => (await callback(item)) ? item : fail))).filter(i=>i!==fail)
}

Demo:

演示:

// Async IIFE
(async function() {
  const myArray = [1, 2, 3, 4, 5]

  // This is exactly what you'd expect to write 
  const results = await filter(myArray, async num => {
    await doAsyncStuff()
    return num > 2
  })

  console.log(results)
})()


// Arbitrary asynchronous function
function doAsyncStuff() {
  return Promise.resolve()
}


// The helper function
async function filter(arr, callback) {
  const fail = Symbol()
  return (await Promise.all(arr.map(async item => (await callback(item)) ? item : fail))).filter(i=>i!==fail)
}

I'll even throw in a CodePen.

我甚至会投入一个CodePen。

#4


8  

Promise Reducer to the rescue!

承诺减速机救援!

[1, 2, 3, 4].reduce((op, n) => {
    return op.then(filteredNs => {
        return new Promise(resolve => {
            setTimeout(() => {
                if (n >= 3) {
                    console.log("Keeping", n);
                    resolve(filteredNs.concat(n))
                } else {
                    console.log("Dropping", n);
                    resolve(filteredNs);
                }
            }, 1000);
        });
    });
}, Promise.resolve([]))
.then(filteredNs => console.log(filteredNs));

Reducers are awesome. "Reduce my problem to my goal" seems to be a pretty good strategy for anything more complex than what the simple tools will solve for you, i.e. filtering an array of things that aren't all available immediately.

减速器非常棒。 “将我的问题减少到我的目标”对于任何比简单工具为您解决的更复杂的事情来说似乎是一个非常好的策略,即过滤一系列并非全部可用的事物。

#5


0  

A valid way to do this (but it seems too messy):

这是一种有效的方法(但它似乎太乱了):

let arr = [1,2,3];

function filter(num) {
  return new Promise((res, rej) => {
    setTimeout(() => {
      if( num === 3 ) {
        res(num);
      } else {
        rej();
      }
    }, 1);
  });
}

async function check(num) {
  try {
    await filter(num);
    return true;
  } catch(err) {
    return false;
  }
}

(async function() {
  for( let num of arr ) {
    let res = await check(num);
    if(!res) {
      let index = arr.indexOf(num);
      arr.splice(index, 1);
    }
  }
})();

Again, seems way too messy.

再次,似乎太乱了。

#6


0  

Late to the game but since no one else mentioned it, Bluebird supports Promise.map which is my go-to for filters requiring aysnc processing for the condition,

在游戏的后期,但由于没有其他人提及它,Bluebird支持Promise.map,这是我需要aysnc处理条件的过滤器,

function filterAsync(arr) {
    return Promise.map(arr, num => {
        if (num === 3) return num;
    })
        .filter(num => num !== undefined)
}