如何顺序执行promises,从数组传递参数?

时间:2017-03-29 01:57:02

标签: javascript

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

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


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

现在,如果我执行上面的语句,它将按顺序运行。在我的实际用例中,数组是动态填充的,我需要为myPromise()中的每个成员执行myArray函数。

如何创建一个“暂停循环”,为数组中的每个项循环,执行myPromise并等待承诺得到解决,然后再继续下一次迭代?

8 个答案:

答案 0 :(得分:34)

如果您可以像创建问题一样创建与数组元素一样多的promise,那么您可以将.then重复应用到折叠中。

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

但是,例如,异步函数不需要:

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

mapSeries(myArray, myPromise)

作为mapSeries的优秀承诺库Bluebird内置:

Promise.mapSeries(myArray, myPromise)

Runnable片段:

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()
)

答案 1 :(得分:8)

不要创建一系列承诺。创建一个返回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()
})()

Promise在创建后立即开始运行。因此,只有在完成当前的承诺之后才能通过构造下一个承诺来确保顺序执行。

答案 2 :(得分:3)

顺序:

您可以使用async await功能按顺序运行promise。这是一个片段

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

平行:

对于并行,您仅可以在循环中调用每个异步函数一次,但是如果您希望获得它们的组合结果,则可以使用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 ;
    })
}


注释:我正在考虑将各个参数作为列表列表,因为应该将多个参数传递给函数中的任何一个,否则,如果您只需要向每个函数传递一个参数,则可以删除点差运算符。

答案 3 :(得分:2)

您可以使用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());

当然,如果您有一个可能出错的承诺,或者如果您需要以前的结果,那么这种简单的方法将无效,因此您可能希望sequencePromises更通用:

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

然后,只要你退回承诺,你就可以做任何你想做的事。

最后,你可能会受益于这个小帮手:

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

将所有这些结合在一起:

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

promiseSeries(myArray, sequencePromises);

这样,您不仅可以处理问题中的案例,还可以处理更复杂的案例。

答案 4 :(得分:1)

你也可以通过递归方法来做到这一点 - 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');
});

答案 5 :(得分:1)

我知道我来晚了,我的回答与其他人的类似。但是我认为我可以发布更清晰的答案,这可能对任何初学者都有帮助。

我们可以使用Promise工厂而不是直接使用Promise。由于使用诺言工厂创建诺言后便立即执行它们,因此我们延迟了诺言的创建。

在此示例中,我创建了5个,在一秒钟后解析。我使用promiseCreator来创建诺言。现在,数组promises使用promiseCreator创建5个promise实例。但是数组promiseFactoriespromiseCreator包装在函数中,因此不会立即调用promise。使用时将调用它。

函数executeSequentially依次执行所有promiseLike

  • 通过promise数组时,结果是promise数组本身并行执行(实际上,它们是在创建后立即执行的,而不是在调用此行时执行)。
  • 当传递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)

答案 6 :(得分:0)

我会使用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>

答案 7 :(得分:0)

你可以迭代元素数组并传递这样的参数:

&#13;
&#13;
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))
);
&#13;
&#13;
&#13;