有没有办法只用一个回调调用递归地进行异步调用?
Is there a way to do asynch calls recursively with only one callback call?
我想知道是否有人知道在 javascript 中递归执行异步调用的方法。我正在尝试一次将 500 个项目的批量数据上传到 cloudant 数据库。我不知道 json 会有多少文档(在 2000-4000 之间)所以我创建了一个递归函数将它分成 500 个块并上传。
insertDBBulkData: function(trgtDB, dbDocData, callback) {
// if length > 500, recursively upload 500 at a time
if(dbDocData.length > 500) {
console.log("File too big. Length " + dbDocData.length)
module.exports.insertDBBulkData(trgtDB, dbDocData.slice(500, dbDocData.length), function(err, body) {
if(err) {
console.log(err);
callback(err, body)
} else {
// only callback on last one
callback(err, body);
}
});
dbDocData = dbDocData.slice(0, 500);
}
trgtDB.bulk({"docs": dbDocData}, function(err, body) {
if(err) {
callback(err);
} else {
console.log("Successfully uploaded " + dbDocData.length + " users. ")
callback(null, "Success!")
}
});
},
问题是:因为我不知道哪个调用会最后结束,所以我不知道何时将响应发送回服务器(我只能做一次)。我尝试过使用 Promises,但据我所知,我不能将 Promises 与这种递归方法一起使用,因为我没有固定次数调用该方法。这是否可以通过延期承诺来实现?
感谢任何帮助。
谢谢!
解决方案:
感谢 Jonas W。我能够在 for 循环中使用多个 promise 来执行此操作。它不使用递归,但效果更好。这是我的解决方案:
insertDBBulkData: function(trgtDB, dbDocData, callback) {
const promises = [];
for(let start = 0; start < dbDocData.length; start += 500) {
console.log("File too big. Index: " + start);
var dbBulkDataPromise = new Promise(function(resolve, reject) {
trgtDB.bulk({"docs": dbDocData.slice(start, start+500)}, function(err, body) {
if(err) {
reject(err);
} else {
resolve("Success")
}
});
});
promises.push(dbBulkDataPromise);
}
Promise.all(promises).then(function(values) {
console.log(values);
var completed = true;
for (message in values) {
if (message != "Success") {
completed = false;
}
}
if (completed) {
callback(null, values)
} else {
console.log("Partial upload")
callback("Partial upload only", null)
}
}).catch(function(err) {
console.log("Error uploading data: " + err);
callback(err, null)
});
},
这是一个使用 promises 的简单示例:
function insertData(trgtDB, dbDocData, callback) {
var batchSize = 500;
while (dbDocData.length > 0) {
var batch = dbDocData.slice(0, batchSize);
dbDocData.splice(0, batchSize);
insertBatch(targetDB, dbDocData.slice(500, dbDocData.length))
.then(function(result) {
// everything inserted just fine
if (dbDocData.length < 1){
callback(yourParams);
}
})
.error(function(err) {
// handle your error
});
}
}
function insertBatch(trgtBD, batch) {
// return a new promise
return new Promise(function(resolve, reject) {
// load into the db
trgtDB.bulk({
"docs": batch
}, function(err, body) {
if (err) {
// there was an error reject the promise
reject(err);
} else {
// everything is good,
resolve("Success!")
}
});
})
}
如果将批量插入包装到一个 promise 中:
function insert(docs) {
return new Promise((resolve, reject) => {
trgtDB.bulk({ docs }, function(err, body) {
if(err) {
reject(err);
} else {
console.log("Successfully uploaded " + docs.length + " users. ")
resolve("Success!")
}
});
});
}
现在您可以遍历大量数据,将其分成块,开始为每个块插入,然后等待所有承诺:
const promises = [], size = 500;
for(let start = 0; start < dbDocData.length; start += size)
promises.push(insert( dbDocData.slice(start, start + size) ));
Promise.all(promises).then(() => {
console.log("all done");
}).catch(/* handle errors */);
我想知道是否有人知道在 javascript 中递归执行异步调用的方法。我正在尝试一次将 500 个项目的批量数据上传到 cloudant 数据库。我不知道 json 会有多少文档(在 2000-4000 之间)所以我创建了一个递归函数将它分成 500 个块并上传。
insertDBBulkData: function(trgtDB, dbDocData, callback) {
// if length > 500, recursively upload 500 at a time
if(dbDocData.length > 500) {
console.log("File too big. Length " + dbDocData.length)
module.exports.insertDBBulkData(trgtDB, dbDocData.slice(500, dbDocData.length), function(err, body) {
if(err) {
console.log(err);
callback(err, body)
} else {
// only callback on last one
callback(err, body);
}
});
dbDocData = dbDocData.slice(0, 500);
}
trgtDB.bulk({"docs": dbDocData}, function(err, body) {
if(err) {
callback(err);
} else {
console.log("Successfully uploaded " + dbDocData.length + " users. ")
callback(null, "Success!")
}
});
},
问题是:因为我不知道哪个调用会最后结束,所以我不知道何时将响应发送回服务器(我只能做一次)。我尝试过使用 Promises,但据我所知,我不能将 Promises 与这种递归方法一起使用,因为我没有固定次数调用该方法。这是否可以通过延期承诺来实现?
感谢任何帮助。 谢谢!
解决方案:
感谢 Jonas W。我能够在 for 循环中使用多个 promise 来执行此操作。它不使用递归,但效果更好。这是我的解决方案:
insertDBBulkData: function(trgtDB, dbDocData, callback) {
const promises = [];
for(let start = 0; start < dbDocData.length; start += 500) {
console.log("File too big. Index: " + start);
var dbBulkDataPromise = new Promise(function(resolve, reject) {
trgtDB.bulk({"docs": dbDocData.slice(start, start+500)}, function(err, body) {
if(err) {
reject(err);
} else {
resolve("Success")
}
});
});
promises.push(dbBulkDataPromise);
}
Promise.all(promises).then(function(values) {
console.log(values);
var completed = true;
for (message in values) {
if (message != "Success") {
completed = false;
}
}
if (completed) {
callback(null, values)
} else {
console.log("Partial upload")
callback("Partial upload only", null)
}
}).catch(function(err) {
console.log("Error uploading data: " + err);
callback(err, null)
});
},
这是一个使用 promises 的简单示例:
function insertData(trgtDB, dbDocData, callback) {
var batchSize = 500;
while (dbDocData.length > 0) {
var batch = dbDocData.slice(0, batchSize);
dbDocData.splice(0, batchSize);
insertBatch(targetDB, dbDocData.slice(500, dbDocData.length))
.then(function(result) {
// everything inserted just fine
if (dbDocData.length < 1){
callback(yourParams);
}
})
.error(function(err) {
// handle your error
});
}
}
function insertBatch(trgtBD, batch) {
// return a new promise
return new Promise(function(resolve, reject) {
// load into the db
trgtDB.bulk({
"docs": batch
}, function(err, body) {
if (err) {
// there was an error reject the promise
reject(err);
} else {
// everything is good,
resolve("Success!")
}
});
})
}
如果将批量插入包装到一个 promise 中:
function insert(docs) {
return new Promise((resolve, reject) => {
trgtDB.bulk({ docs }, function(err, body) {
if(err) {
reject(err);
} else {
console.log("Successfully uploaded " + docs.length + " users. ")
resolve("Success!")
}
});
});
}
现在您可以遍历大量数据,将其分成块,开始为每个块插入,然后等待所有承诺:
const promises = [], size = 500;
for(let start = 0; start < dbDocData.length; start += size)
promises.push(insert( dbDocData.slice(start, start + size) ));
Promise.all(promises).then(() => {
console.log("all done");
}).catch(/* handle errors */);