使用承诺内部循环的顺序调用



我想使用promise执行函数的同步操作。我有一个循环,将要插入的数据传递给insert函数,插入一行后,我想检查 no。表中存在行数,因此我正在执行select操作。

但问题是如果有 3 条记录,那么它会插入所有 3 条记录,然后执行我的 select 函数。 我想要的是插入一个记录选择函数被调用后。

这是我的伪代码,因为整个代码涉及大量操作

for(var i=0; data.length ; i++){
self.executeFeedbackTrack(data);
}

executeFeedbackTrack:function(callInfo){
var self=this;
return  self.insertFeedbackTrack(callInfo).then(function(data){
console.log("insertFeedbackTrack status "+status);
return self.getFeedbackTrack();
});
},
getFeedbackTrack :function(){
return new Promise(function(resolve,reject){
var objDBFeedbackTrack = new DBFeedbackTrack();
objDBFeedbackTrack.selectFeedbackTrack(function(arrayCallRegisters){
if(arrayCallRegisters){
console.log("notification.js no. of feedbacks "+arrayCallRegisters.length);
resolve(arrayCallRegisters.length);
}
});
});

},
insertFeedbackTrack :function(callInfo){
return new Promise(function(resolve,reject){
var objDBFeedbackTrack = new DBFeedbackTrack();
objDBFeedbackTrack.insertFeedbackTrack(callInfo.callNumber,callInfo.callServiceType,function(status){
resolve(status);
$('#loader').hide();
});
});
}

前面的答案很好,但是如果你使用的是nodejs或babel,或者你只使用现代浏览器。您可以使用异步等待对,它是 es8 的东西。

let insertFeedbackTrack = function(){ return new Promise(/***/)};
let getFeedbackTrack = function(){ return new Promise(/***/)};
let processResult = async function(data){
let feedbacks = [];
for(let i=0;i<data.length;i++){
let insertedResult = await insertFeedbackTrack(data[i]);//perhaps you will return an id;
let feedbackTrack = await getFeedbackTrack(insertedResult.id);
feedbacks.push(feedbackTrack);
}
return feedbacks;
} 
processResult(data).then(/** do stuff */)

在我看来,这是由执行一系列异步插入引起的,并假设在执行insert n+1之前调用了insert nget(在.then()内部)。但是,我不知道在 JavaScript 中有任何这样的保证;我所熟悉的只是then n将在insert n之后调用,而不是在insert n+1之前调用。

我的建议是避免混合使用传统代码和基于回调的代码,而是将迭代步骤放在getFeedbackTrack().then中。假设对问题的这种理解是正确的,那么类似以下内容的内容应该有效:

function iterate(i) {
if (i < data.length) {
obj.insertFeedbackTrack(data[i]).then(function(insertResult) {
self.getFeedbackTrack().then(function(getResult) {
// this line is the important one, replacing the `for` loop earlier
iterate(i+1);
});
});
}
}
iterate(0);

通过这样做,可以保证在当前select成功执行之前不会发生下一个元素的insert

当然,您可能还希望对其进行重组以使用链式.then而不是嵌套;我使用嵌套而不是链式来强调回调的顺序。

这可以通过使用非常方便的JS库Ramda来解决。概念是使用两种方法,一种是R.partial,另一种是R.pipeP

首先从data数组创建一个 promise 数组,如下所示。

var promises = data.map(function(i) {
return R.partial(sample, [i])
});

然后你可以把这个承诺传递给R.pipeP,这样就可以一个接一个地执行。

var doOperation = R.pipeP.apply(this, promises)

请执行所附的以下代码片段。

// Sample promise returning function
function sample(d) {
return new Promise(function(resolve, reject){
setTimeout(function() {
console.log('resolved for:' + d);
resolve(d);
}, 1000)
})
}
// Sample data
var data = [1, 2, 3, 4, 5]
// Converting data array to promise array
var promises = data.map(function(i) {
return R.partial(sample, [i])
});
var doOperation = R.pipeP.apply(this, promises)
doOperation();
<script src="https://cdnjs.cloudflare.com/ajax/libs/ramda/0.25.0/ramda.min.js"></script>

所以在您的情况下,代码将如下所示

var promises = data.map(function(i) {
return R.partial(self.executeFeedbackTrack, [i])
});
var doOperation = R.pipeP.apply(this, promises)
doOperation();

如果使用生成器函数,我会在这种情况下使用 yield。

for(var i = 0; i < order.tasks.length; i++){
if(order.tasks[i].customer_id === 0){
var name = order.tasks[i].customer_name.split(" ")
const customers = yield db.queryAsync(
`INSERT INTO customers(
business_id)
VALUES(?)
`,[order.business_id])
}
}

否则,我在回调的情况下使用自调用函数。

var i = 0;
(function loop() {
if (i < data.length) {
task_ids1.push([order.tasks[i].task_id])
i++;
loop();
}
}());

以下是我在循环中按顺序调用承诺的方式(我使用的是 ES7)。 首先,让我们定义一些基本数据:

const data = [0,1,2,3];

然后,让我们模拟一些长时间运行的进程,让我们创建一个返回 Promise 的函数(你可以将其视为模拟的网络请求,或任何适合你需求的函数)

const promiseExample = (item) =>
new Promise((res) => {
setTimeout(() => {
console.log('resolved ', item);
res(item);
}, 1000);
});

现在,让我们创建一个承诺数组。下一行代码的作用是:对于数组数据中的每个项目,返回一个承诺工厂。promise 工厂是一个函数,它包装某个 promise 而不运行它。

const funcs = data.map(item => async () => await promiseExample(item));

现在,实际代码从这里开始。我们需要一个执行实际序列化的函数。由于它必须处理 promiseFactories 数组,因此我将其拆分为两个函数,一个用于序列化单个 promise,另一个用于处理 promiseFactory 数组。

const serializePromise = promiseFactoryList =>
promiseFactoryList.reduce(serialize, Promise.resolve([]));
const serialize = async (promise, promiseFactory) => {
const promiseResult = await promise;
const res = await promiseFactory();
return [...promiseResult, res];
};

现在,您可以简单地这样称呼它:

serializePromise(funcs).then(res => {
console.log('res', res);
});

如您所见,代码非常简单,优雅,功能强大,并且不需要任何外部依赖项。我希望这能回答您的问题并对您有所帮助!

const serializePromise = promiseFactoryList =>
promiseFactoryList.reduce(serialize, Promise.resolve([]));
const serialize = async (promise, promiseFactory) => {
const promiseResult = await promise;
const res = await promiseFactory();
return [...promiseResult, res];
};
const data = [0,1,2,3];
const promiseExample = (item) =>
new Promise((res) => {
	setTimeout(() => {
	  console.log('resolved ', item);
	  res(item);
}, 1000);
});
const funcs = data.map(item => async () => await promiseExample(item))
serializePromise(funcs).then(res => {
console.log('res', res);
});

我最近遇到了这个问题,并如下图所示解决了它。这与@Ethan Kaminsky的答案非常相似,但只使用回调。这对于人们出于任何原因避免承诺可能很有用。

在我的应用程序中,异步函数可能会失败,可以安全地重试;我包含此逻辑是因为它很有用,并且不会使例程过于复杂,但在示例中没有执行。

// Some callback when the task is complete
function cb(...rest) { window.alert( `${rest.join(', ')}` ) }
// The data and the function operating on the data
// The function calls "callback(err)" on completion
const data = [ 'dataset1', 'dataset2', 'dataset3' ]
const doTheThing = (thingDone) => setTimeout( thingDone, 1000 )
let i = -1                    // counter/interator for data[]
let retries = 20              // everything fails; total retry #
// The do-async-synchronously (with max retries) loop
function next( err ) {
if( err ) {
if( ! --retries ) return cb( 'too many retries' )
} else if( ! data[++i] ) return cb( undefined, 'done-data' )
console.log( 'i is', i, data[i] )
doTheThing( next, data[i] )       // callback is first here
}
// start the process
next()

最新更新