使用 Bluebird.mapSeries 按顺序处理一组 API 调用
Process an array of API calls sequentially with Bluebird.mapSeries
我一直在尝试的是按顺序处理 API 个调用,这些调用被推入一个数组。
function resolveApi(callback) {
return new Promise(function(resolve, reject) { resolve(callback) });
}
function callApi(){
var promises = [];
promises.push(resolveApi(Restangular.one("api/blahblah",{/*input data*/})));
promises.push(resolveApi(Restangular.one("api/another/api",{/*input data*/})));
//there are other similar API calls.
//Restangular is supposed to return an API call
promises = Bluebird.mapSeries(promises, function(item, index) {
return item;
});
promises.then(function(){/* the end */});
}
目标是处理 promises
数组中的每个 API 调用,但上面的代码没有这样做。 (即 API 调用几乎同时调用,而每个 API 调用应该等到前一个调用完成。)
你看到我哪里做错了吗?
如有任何建议,我们将不胜感激。
resolveApi(Restangular.one("api/blahblah",{/*input data*/}))
不是真正的 Promise,它将立即取消 callback
我猜你想解决 ajax 成功后, Promise.all
并行运行这是最快的方式,像这样:
Promise.all([
Restangular.one("api/blahblah", { /*input data*/ }),
Restangular.one("api/blahblah", { /*input data*/ }),
...
]).then((resultArray) => {
//all finish
console.log(resultArray)
}).catch((e) => {
console.log(e)
})
或依次
Promise.mapSeries([
["api/blahblah", { /*input data*/ }],
["api/blahblah", { /*input data*/ }],
...
], (item) => Restangular.one(item[0], item[1])).then(() => {
//done
}).catch((e) => {
})
等等,你可能想在下一个ajax中使用一个ajax的结果,你可以使用
https://www.npmjs.com/package/chain-promise,代码可能看起来像
var chainPromise = require("chain-promise");
const params = [
["api/blahblah"],
["api/blahblah"],
]
chainPromise(params.map((p) => {
return (data) => Restangular.one(p[0], data)
}), initData).then(() => {
console.log('done')
}).catch((e) => {
console.log(e)
})
好吧,你可以使用一个小的递归实用程序来一个接一个地对 promise 进行排序。我这里有两个实现
- 这个将处理任何抛出的错误并继续下一个承诺。我使用
.finally()
所以请确保你的浏览器是最新的。
var simulatePromisifiedAPI = (delay,data) => new Promise((v,x) => setTimeout(_ => Math.random() > 0.1 ? v(data) : x(`error @ ${data}`), delay)),
promises = Array(10).fill(simulatePromisifiedAPI),
sequencePromises = ([api,...apis]) => apis.length ? api(250,`task no ${10-apis.length}`).then(v => console.log(`Doing something with the API data ${v}`))
.catch(e => console.log(`Opps... Just got ${e}`))
.finally(_ => sequencePromises(apis))
: api(250,"final").then(v => console.log(`Doing something with the API data ${v}`))
.catch(e => console.log(`Opps... Just got ${e}`));
sequencePromises(promises);
.as-console-wrapper {
height : 100%;
max-height: 100% !important;
}
或者,如果链中的任何 promise 被拒绝,您可能只想切断排序过程。然后只需删除 .finally()
部分并将 sequencePromises(apis)
指令插入 .then()
阶段。
var simulatePromisifiedAPI = (delay,data) => new Promise((v,x) => setTimeout(_ => Math.random() > 0.1 ? v(data) : x(`error @ ${data}`), delay)),
promises = Array(10).fill(simulatePromisifiedAPI),
sequencePromises = ([api,...apis]) => apis.length ? api(250,`task no ${10-apis.length}`).then(v => (console.log(`Doing something with the API data ${v}`), sequencePromises(apis)))
.catch(e => console.log(`Opps... Just got ${e}`))
: api(250,"final").then(v => console.log(`Doing something with the API data ${v}`))
.catch(e => console.log(`Opps... Just got ${e}`));
sequencePromises(promises);
.as-console-wrapper {
height : 100%;
max-height: 100% !important;
}
好的,如果 Restangular.one()
return 是一个承诺(您的评论现在表明)并且您想按顺序处理这些 api 调用(一个接一个),那么您只需必须正确组织 Bluebird 的 .mapSeries()
的参数。它需要一个数据数组和一个函数(对数据进行操作并且 return 是一个承诺)。
所以,让我们这样组织你的东西:
const data = [
{endPoint: "http://somedomain.com/api/blahblah", args: [1,2]},
{endPoint: "http://somedomain.com/api/another/api", args: ["a", "b"]},
{endPoint: "http://somedomain.com/api/yetanother", args: ["abc"]},
];
Promise.mapSeries(data, function(dataObj) {
return Restangular.one(dataObj.endPoint, ...dataObj.args);
}).then(results => {
// array of results here
console.log(results);
}).catch(err => {
// error occurred here
console.log(err);
});
既然你说 Restangular.one()
已经 return 是一个承诺,我看不出有任何理由需要将它包装在你的 resolveApi()
函数中。这不是必需的,我们当然不需要将 promise 变成一个简单的回调来使用 .mapSeries()
.
您的实现中缺少很多东西,但最主要的是您立即调用所有异步操作,然后尝试管理承诺。那不会对您的操作进行排序。并行运行它们。此外,如果您给 .mapSeries()
一个承诺数组,它无法对操作进行排序,因为它们都已经开始了。为了让它管理异步操作何时开始以便它可以对它们进行排序,您必须向它传递一个数据数组和一个要调用的函数,该函数传递了该数据的一项并将 return 一个承诺。然后,它可以一次一个地调用它们,您的操作将一次一个地开始。
我一直在尝试的是按顺序处理 API 个调用,这些调用被推入一个数组。
function resolveApi(callback) {
return new Promise(function(resolve, reject) { resolve(callback) });
}
function callApi(){
var promises = [];
promises.push(resolveApi(Restangular.one("api/blahblah",{/*input data*/})));
promises.push(resolveApi(Restangular.one("api/another/api",{/*input data*/})));
//there are other similar API calls.
//Restangular is supposed to return an API call
promises = Bluebird.mapSeries(promises, function(item, index) {
return item;
});
promises.then(function(){/* the end */});
}
目标是处理 promises
数组中的每个 API 调用,但上面的代码没有这样做。 (即 API 调用几乎同时调用,而每个 API 调用应该等到前一个调用完成。)
你看到我哪里做错了吗?
如有任何建议,我们将不胜感激。
resolveApi(Restangular.one("api/blahblah",{/*input data*/}))
不是真正的 Promise,它将立即取消 callback
我猜你想解决 ajax 成功后, Promise.all
并行运行这是最快的方式,像这样:
Promise.all([
Restangular.one("api/blahblah", { /*input data*/ }),
Restangular.one("api/blahblah", { /*input data*/ }),
...
]).then((resultArray) => {
//all finish
console.log(resultArray)
}).catch((e) => {
console.log(e)
})
或依次
Promise.mapSeries([
["api/blahblah", { /*input data*/ }],
["api/blahblah", { /*input data*/ }],
...
], (item) => Restangular.one(item[0], item[1])).then(() => {
//done
}).catch((e) => {
})
等等,你可能想在下一个ajax中使用一个ajax的结果,你可以使用 https://www.npmjs.com/package/chain-promise,代码可能看起来像
var chainPromise = require("chain-promise");
const params = [
["api/blahblah"],
["api/blahblah"],
]
chainPromise(params.map((p) => {
return (data) => Restangular.one(p[0], data)
}), initData).then(() => {
console.log('done')
}).catch((e) => {
console.log(e)
})
好吧,你可以使用一个小的递归实用程序来一个接一个地对 promise 进行排序。我这里有两个实现
- 这个将处理任何抛出的错误并继续下一个承诺。我使用
.finally()
所以请确保你的浏览器是最新的。
var simulatePromisifiedAPI = (delay,data) => new Promise((v,x) => setTimeout(_ => Math.random() > 0.1 ? v(data) : x(`error @ ${data}`), delay)),
promises = Array(10).fill(simulatePromisifiedAPI),
sequencePromises = ([api,...apis]) => apis.length ? api(250,`task no ${10-apis.length}`).then(v => console.log(`Doing something with the API data ${v}`))
.catch(e => console.log(`Opps... Just got ${e}`))
.finally(_ => sequencePromises(apis))
: api(250,"final").then(v => console.log(`Doing something with the API data ${v}`))
.catch(e => console.log(`Opps... Just got ${e}`));
sequencePromises(promises);
.as-console-wrapper {
height : 100%;
max-height: 100% !important;
}
或者,如果链中的任何 promise 被拒绝,您可能只想切断排序过程。然后只需删除 .finally()
部分并将 sequencePromises(apis)
指令插入 .then()
阶段。
var simulatePromisifiedAPI = (delay,data) => new Promise((v,x) => setTimeout(_ => Math.random() > 0.1 ? v(data) : x(`error @ ${data}`), delay)),
promises = Array(10).fill(simulatePromisifiedAPI),
sequencePromises = ([api,...apis]) => apis.length ? api(250,`task no ${10-apis.length}`).then(v => (console.log(`Doing something with the API data ${v}`), sequencePromises(apis)))
.catch(e => console.log(`Opps... Just got ${e}`))
: api(250,"final").then(v => console.log(`Doing something with the API data ${v}`))
.catch(e => console.log(`Opps... Just got ${e}`));
sequencePromises(promises);
.as-console-wrapper {
height : 100%;
max-height: 100% !important;
}
好的,如果 Restangular.one()
return 是一个承诺(您的评论现在表明)并且您想按顺序处理这些 api 调用(一个接一个),那么您只需必须正确组织 Bluebird 的 .mapSeries()
的参数。它需要一个数据数组和一个函数(对数据进行操作并且 return 是一个承诺)。
所以,让我们这样组织你的东西:
const data = [
{endPoint: "http://somedomain.com/api/blahblah", args: [1,2]},
{endPoint: "http://somedomain.com/api/another/api", args: ["a", "b"]},
{endPoint: "http://somedomain.com/api/yetanother", args: ["abc"]},
];
Promise.mapSeries(data, function(dataObj) {
return Restangular.one(dataObj.endPoint, ...dataObj.args);
}).then(results => {
// array of results here
console.log(results);
}).catch(err => {
// error occurred here
console.log(err);
});
既然你说 Restangular.one()
已经 return 是一个承诺,我看不出有任何理由需要将它包装在你的 resolveApi()
函数中。这不是必需的,我们当然不需要将 promise 变成一个简单的回调来使用 .mapSeries()
.
您的实现中缺少很多东西,但最主要的是您立即调用所有异步操作,然后尝试管理承诺。那不会对您的操作进行排序。并行运行它们。此外,如果您给 .mapSeries()
一个承诺数组,它无法对操作进行排序,因为它们都已经开始了。为了让它管理异步操作何时开始以便它可以对它们进行排序,您必须向它传递一个数据数组和一个要调用的函数,该函数传递了该数据的一项并将 return 一个承诺。然后,它可以一次一个地调用它们,您的操作将一次一个地开始。