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

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

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

我如何制作一个“暂停循环”,它将循环数组中的每个项目,执行 myPromise并等待承诺被解决,然后继续下一个迭代?

63838 次浏览

如果你愿意像这个问题中的情况一样,创建尽可能多的承诺数组元素,那么你可以把 .then的重复应用整齐地折叠起来:

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

但是如果给予支持,异步函数是一个更好的选择。它具有良好的可读性,并且具有 O (1)而不是 O (n)内存开销。

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


forEachSeries(myArray, myPromise)

如果希望以数组形式收集返回值,则为:

const mapSeries = async (iterable, fn) => {
const results = []


for (const x of iterable) {
results.push(await fn(x))
}


return results
}

或者,如果没有异步函数支持,

const mapSeries = (iterable, fn) => {
const iterator = iterable[Symbol.iterator]()
const results = []
const go = () => {
const {value, done} = iterator.next()


if (done) {
return results
}


return fn(value).then(mapped => {
results.push(mapped)
return go()
})
}


return Promise.resolve().then(go)
}

可运行代码片段:

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


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


forEachSeries(myArray, myPromise)
.then(() => {
console.log('all done!')
})

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

你可以用 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);

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

您可以迭代元素数组并像下面这样传递参数:

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.map(
async (num) => await MyPromiseFunction(num)
.then(message => logMessage(num, message))
.catch(reason => logMessage(num, reason))
)

不要创建一个承诺数组。创建一个返回承诺的函数数组。

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

承诺在创造之后立即开始运行。因此,只有在完成当前承诺之后才构造下一个承诺,从而确保顺序执行。

你也可以通过递归的方法来实现—— 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,它在一秒钟后解析。我用一个承诺来创造承诺。现在,数组 promises使用 promiseCreator创建5个承诺实例。但是数组 promiseFactoriespromiseCreator包装在一个函数中,因此不会立即调用諾。它在使用时被调用。

函数 executeSequentially按顺序执行所有 promiseLike

  • 当传递 promise数组时,结果是 promise数组本身并行执行(实际上它们是在创建后立即执行的,而不是在调用这一行时执行)。
  • promiseFactory数组被传递结果是新的承诺是创建时,早先的承诺已经完成了他们的执行。

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)

顺序:

您可以使用 async await特性来顺序运行承诺

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




注意 : 我考虑将各个参数作为一个列表,因为多个参数应该传递给任何一个函数,否则,如果只需要向每个函数传递一个参数,那么可以删除扩展操作符。