简体   繁体   English

如何按顺序执行承诺,从数组传递参数?

[英]How to execute promises sequentially, passing the parameters from an array?

var myArray = [1, 2, 3, 4, 5, 6]

function myPromise(num){
  return new Promise(res => {
    window.setTimeout(()=>{
      res(  console.log("done: " + num)  )
    },2000)
  })
}


myPromise(myArray[0])
  .then(x => myPromise(myArray[1]))
  .then(x => myPromise(myArray[2]))
  .then(x => myPromise(myArray[3]))
  .then(x => myPromise(myArray[4]))
  .then(x => myPromise(myArray[5]))

Right now, if I execute the statement above, it will run sequentially.现在,如果我执行上面的语句,它将按顺序运行。 In my actual use case the array is dynamically populated and I need to execute the myPromise() function for each member in myArray .在我的实际用例中,数组是动态填充的,我需要为myArray中的每个成员执行myPromise() function 。

How can I make a "pauseable loop" that will loop for each item in the array, execute myPromise and wait for the promise to be resolved before continuing to the next iteration?如何创建一个“可暂停循环”,它将为数组中的每个项目循环,执行myPromise并等待 promise 得到解决,然后再继续下一次迭代?

You can make the repeated application of .then into a fold pretty neatly if you're okay with creating as many promises as array elements as is the case in the question:如果您可以像问题中的情况一样创建与数组元素一样多的承诺,您可以将.then的重复应用非常整齐地折叠起来:

myArray.reduce(
  (p, x) =>
    p.then(_ => myPromise(x)),
  Promise.resolve()
)

but an async function, for example, doesn't require that:但是,例如,异步函数不需要:

const mapSeries = async (iterable, action) => {
  for (const x of iterable) {
    await action(x)
  }
}

mapSeries(myArray, myPromise)

which is built into the excellent promise library Bluebird as mapSeries :它作为mapSeries内置于优秀的承诺库 Bluebird 中:

Promise.mapSeries(myArray, myPromise)

Runnable snippet:可运行的片段:

 const myArray = [1, 2, 3, 4, 5, 6] const sleep = ms => new Promise(res => { setTimeout(res, ms) }) const myPromise = num => sleep(500).then(() => { console.log('done: ' + num) }) myArray.reduce( (p, x) => p.then(_ => myPromise(x)), Promise.resolve() )

Don't create an array of promises.不要创建一系列承诺。 Create an array of functions returning a promise.创建一个返回承诺的函数数组。

const f = x => new Promise(resolve => setTimeout(() => resolve(console.log(x)), 2000))

(async () => {
    for (let job of [1, 2, 3, 4, 5, 6].map(x => () => f(x)))
        await job()
})()

Promises start running immediately after creation.承诺在创建后立即开始运行。 Therefore, sequential execution is ensured by constructing the next promise only after finishing the current one.因此,只有在完成当前的承诺后,才能通过构建下一个承诺来确保顺序执行。

Also you can do it via recursive approach - executeSequentially calls itself:您也可以通过递归方法来实现 - executeSequentially调用自身:

 function createPromise(x) { return new Promise(res => { setTimeout(() => { console.log(x) res(x); }, x * 1000) }) } function executeSequentially(array) { return createPromise(array.shift()) .then(x => array.length == 0 ? x : executeSequentially(array)); } console.time('executeSequentially'); executeSequentially([1, 2, 3]).then(x => { console.log('last value: ' + x); console.timeEnd('executeSequentially'); });

Sequential:顺序:

you can use async await features to run promises sequentially .您可以使用async await功能按顺序运行承诺。 here's a snippet这是一个片段

async function chainPromiseCalls(asyncFunctions=[],respectiveParams=[]){

    for(let i=0;i<asyncFunctions.length;i++){
        const eachResult = await asyncFunctions[i](...respectiveParams[i]);
        // do what you want to do with each result 
       
    }

    return ;
}

Parallel:平行线:

for parallel you can just call each async function once in a loop , but if you do want to get their combined result , you can use Promise.all对于并行,您可以只在循环中调用每个异步函数一次,但如果您确实想获得它们的组合结果,则可以使用Promise.all

function parallelPromiseCalls(asyncFunctions=[],respectiveParams=[]){
    return Promise.all(asyncFunctions.map((func,index)=>func(...respectiveParams[index])))
       .then(resultsList=>{
        resultsList.forEach((result,index)=>{
           // do what you want to do with each result in the list
        })
        return ;
    })
}


note : I am considering respective parameters as an list of lists since multiple parameters should be passed to any one of the function , else if you have to pass only a single parameter to each then you can remove the spread operator.注意:我将各个参数视为列表列表,因为应将多个参数传递给任何一个函数,否则如果您只需要向每个参数传递一个参数,则可以删除扩展运算符。

I know I am very late, and my answer is similar to what others have posted.我知道我很晚了,我的回答与其他人发布的相似。 But I thought I could post a clearer answer which may help any beginner.但我想我可以发布一个更清晰的答案,这可能对任何初学者都有帮助。

Instead of using promises directly, we can use promise factory.我们可以使用promise factory代替直接使用promise。 Since the promise starts executing as soon as they are created using promise factory we delay the creation of promise.由于承诺在使用承诺工厂创建后立即开始执行,因此我们延迟了承诺的创建。

In this example I create 5 which resolve after a second.在这个例子中,我创建了 5 秒后解析。 I use a promiseCreator to create promises.我使用 promiseCreator 来创建承诺。 Now the array promises uses promiseCreator to create 5 instances of promises.现在数组promises使用promiseCreator创建 5 个 promises 实例。 But array promiseFactories wraps promiseCreator in a function so promise is not invoked immediately.但阵列promiseFactories包裹promiseCreator的功能,所以承诺不会立即调用。 It is invoked when used.它在使用时被调用。

Function executeSequentially executes all promiseLike sequentially.函数executeSequentially依次执行所有promiseLike

  • When promise array is passed result is promise array itself executes parallely (actually they executed as soon as they are created, not when this line is called).promise数组被传递时,结果是promise数组本身并行执行(实际上它们在创建后立即执行,而不是在调用此行时执行)。
  • When promiseFactory array is passed result is new Promise is created when earlier promise has completed their execution.promiseFactory数组被传递时,结果是新的 Promise 在先前的 promise 完成执行时被创建。

 const promiseCreator = (i, time, text) => { return new Promise(resolve => setTimeout( () => resolve(console.log(`${i} ${text}`)), time) ); } const promises = [ promiseCreator(1, 1000, "parallel"), promiseCreator(2, 1000, "parallel"), promiseCreator(3, 1000, "parallel"), promiseCreator(4, 1000, "parallel"), promiseCreator(5, 1000, "parallel"), ] const promiseFactories = [ () => promiseCreator(1, 1000, "sequential"), () => promiseCreator(2, 1000, "sequential"), () => promiseCreator(3, 1000, "sequential"), () => promiseCreator(4, 1000, "sequential"), () => promiseCreator(5, 1000, "sequential"), ] function executeSequentially(promiseLikeArray) { var result = Promise.resolve(); promiseLikeArray.forEach(function (promiseLike) { result = result.then(promiseLike); }); return result; } executeSequentially(promises) executeSequentially(promiseFactories)

You could use Array.reduce .你可以使用Array.reduce

//type: [number]
var myArray = [1, 2, 3, 4, 5, 6] //doesn't really matter

//type: number -> Promise<number>
function myPromise(num){
  return new Promise((resolve) => {
    window.setTimeout(()=>{
      resolve(console.log("done: " + num)  )
    },2000)
  })
}

//Array.reduce has type: [a] ~> ((b, a) -> b), b) -> b
//So it can have type:
//[number] ~> ((Promise<number>, number) -> Promise<number>), Promise<number>) -> Promise<number>
//Therefore we need to give reduce a function that takes a Promise 
//resolving to a number and a number which makes a new promise.
//This is the function we want:

function sequencePromises(promise, number) {
  return new Promise((resolve) => {
    resolve(promise.then(_ => myPromise(number)));
  });
} 

myArray.reduce(sequencePromises, Promise.resolve());

Of course, this simplistic approach won't work if you have a promise which can error, or if you need previous results, so you might want to make sequencePromises more generic:当然,如果您有一个可能出错的 promise,或者如果您需要以前的结果,这种简单的方法将不起作用,因此您可能希望使sequencePromises更通用:

function genericSequencePromises(promiseFunction) {
  return (promise, parameter) => {
    return new Promise((resolve, reject) => 
                         return promiseFunction(resolve, 
                                                reject, 
                                                promise, 
                                                parameter));
  }
}

Then you can do whatever you want as long as you return a Promise.然后你可以做任何你想做的事,只要你返回一个 Promise。

Finally, you might benefit from this little helper:最后,您可能会从这个小帮手中受益:

function promiseSeries(array, reducer) {
  return array.reduce(reducer, Promise.resolve());
}

Bringing it all together:把它放在一起:

let sequencePromises = genericSequencePromises((resolve, reject, promise, num) => {
  resolve(promise.then(_ => console.log(`done: ${num}`)));
}

promiseSeries(myArray, sequencePromises);

This way, you can not only handle the case in your question, but much more complex cases.这样,您不仅可以处理问题中的案例,还可以处理更复杂的案例。

I would use babel and do it this way:我会使用babel并这样做:

 let args = [1, 2, 3]; const myPromise = async x => console.log('arg:',x); const test = async () => { for (let task of args.map(myPromise)) await task; } test().then(console.log('Done'));
 <script src="https://unpkg.com/babel-standalone@6.24.0/babel.min.js"></script>

You can iterate over the array of elements and pass the params like this:您可以遍历元素数组并像这样传递参数:

 const arr = [1, 2, 3, 4, 5, 6]; const MyPromiseFunction = num => new Promise((resolve, reject) => { // Your logic... setTimeout(() => num <= 4 ? resolve('Success!') : reject('Rejected!'), 1000 * num); }); const logMessage = (num, msg) => console.log(`For number ${num} promise result: ${msg}`); arr.forEach( num => MyPromiseFunction(num) .then(message => logMessage(num, message)) .catch(reason => logMessage(num, reason)) );

 const myArray = [1, 2, 1000, 4, 5000, 6] const sleep = ms => new Promise(res => { setTimeout(res, ms) }) const myPromise = num => sleep(num).then(() => { console.log('done: ' + num) }) myArray.reduce( (p, x) => p.then(_ => myPromise(x)), Promise.resolve() )

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM