节点回调地狱+异步通知
Node Callback hell + async advice
我是 node 的新手,最近阅读了大量关于如何最好地避免回调地狱的书。由于我的新手能力,我在我的代码中积累了一些金字塔,所以我点击 google 看看如何避免它。
我正在使用 Salesforce Marketing Cloud 构建应用程序 API...
这是我的场景。
- 检查我的帐户中是否存在特定文件夹。
- 如果存在 return 文件夹的 ID
- 如果它不存在,搜索父文件夹并获取它的 ID
- 创建文件夹并使用 ParentFolders ID 作为 属性。
所以在旧学校代码中,这非常简单,只需按顺序执行步骤 1-4。
在节点中,我最终得到了一个带有回调函数的函数。
感谢 Google 我遇到了异步库,但不太确定如何最好地利用它。
这是我的一段代码,它是我从运行服务器的 main.js 文件调用的更大模块的一部分
MyHelper.prototype.test = function(name, req, callback) {
async.series([
function(next){
etHelper.folder_find("dataextension", name, next);
},function(next){
etHelper.folder_find("dataextension", "Data Extensions", next);
}
],
function(err, results){
console.log(results)
});
};
我应该使用异步的瀑布方法吗?如果是这样,我将如何检查步骤 1 中的 ID,如果它不是 null exit
不确定它是否有帮助,但这是我从我的 ETHelper 方法构建的响应
[ { Success: false,
Status: 'Warning',
Message: 'Folder Not Found',
Type: 'dataextension',
Name: '10513542_SQLHelper_Test',
ID: null },
{ Success: true,
Status: 'OK',
Message: 'Folder Found',
Type: 'dataextension',
Name: 'Data Extensions',
ID: '1040721' } ]
我真的很想在这里学习,但我对所遇到的信息有点不知所措。似乎有 100 种不同的做事方式,很难知道选择哪一种。
最后,我不想搞砸这个 post 但这是我想出的原始方法来处理这个,我知道这是一团糟。
SQLHelper.prototype.checkInstall = function(name, req, callback) {
var response = new Object();
var deDone = false;
var qDone = false;
/*
*********************************************************************************************************
*
* This function will look for the SQLHelper folders. If they are not found they will be added
*
*********************************************************************************************************
*/
//Setup the DEFolder Lookup Filter
var deFolderParms = {
objectType: "DataFolder",
props: ["Name", "ID", "ContentType"],
filter: {
leftOperand: {
leftOperand: 'Name',
operator: 'equals',
rightOperand: name
},
operator: 'AND',
rightOperand:
{
leftOperand: 'ContentType',
operator: 'equals',
rightOperand: "dataextension"
}
}
};
//Look for the Folder
etHelper.getByName(deFolderParms, SoapClient, function(err, deFolderResponse){
//console.log(deFolderResponse);
if(deFolderResponse.length <=0)
{
//Folder Does not exist so create it
//But first we need the Parent Folder
var deParentFolderParms = {
objectType: "DataFolder",
props: ["Name", "ID", "ContentType"],
filter: {
leftOperand: {
leftOperand: 'Name',
operator: 'equals',
rightOperand: "Data Extensions"
},
operator: 'AND',
rightOperand:
{
leftOperand: 'ContentType',
operator: 'equals',
rightOperand: "dataextension"
}
}
};
etHelper.getByName(deParentFolderParms, SoapClient, function(err, deParentFolderResponse){
//console.log(deParentFolderResponse);
if(deParentFolderResponse.length > 0){
var newFolderParms = {
objectType: "DataFolder",
props: {
Name: name,
CustomerKey: name,
Description: "This folder is only to be used by the SQL Helper Application",
ContentType: "dataextension",
IsActive: true,
IsEditable: true,
AllowChildren: false,
ParentFolder:{
ID: deParentFolderResponse[0].ID
},
Client:{
ID: req.session.fuel.mid
}
},
options: {}
};
etHelper.create(newFolderParms, SoapClient, function(err, newFolderResponse){
//console.log(newFolderResponse);
if(newFolderResponse.length > 0){
deDone = true;
response.DEFolderID = newFolderResponse[0].NewID;
response.Status = true;
if(qDone)
{
callback(response);
}
}
else{
response.Status = false;
callback(response);
}
});
}
else
{
response.Status = false;
callback(response);
}
});
}
else
{
deDone = true;
response.DEFolderID = deFolderResponse[0].ID;
response.Status = true;
if(qDone)
{
callback(response);
}
}
});
//Setup the DEFolder Lookup Filter
var qFolderParms = {
objectType: "DataFolder",
props: ["Name", "ID", "ContentType"],
filter: {
leftOperand: {
leftOperand: 'Name',
operator: 'equals',
rightOperand: name
},
operator: 'AND',
rightOperand:
{
leftOperand: 'ContentType',
operator: 'equals',
rightOperand: "queryactivity"
}
}
};
//Look for the Folder
etHelper.getByName(qFolderParms, SoapClient, function(err, qFolderResponse){
//console.log(qFolderResponse);
if(qFolderResponse.length <=0)
{
//Folder Does not exist so create it
//But first we need the Parent Folder
var qParentFolderParms = {
objectType: "DataFolder",
props: ["Name", "ID", "ContentType"],
filter: {
leftOperand: {
leftOperand: 'Name',
operator: 'equals',
rightOperand: "Query"
},
operator: 'AND',
rightOperand:
{
leftOperand: 'ContentType',
operator: 'equals',
rightOperand: "queryactivity"
}
}
};
etHelper.getByName(qParentFolderParms, SoapClient, function(err, qParentFolderResponse){
//console.log(qParentFolderResponse);
if(qParentFolderResponse.length > 0){
var qnewFolderParms = {
objectType: "DataFolder",
props: {
Name: name,
CustomerKey: name,
Description: "This folder is only to be used by the SQL Helper Application",
ContentType: "queryactivity",
IsActive: true,
IsEditable: true,
AllowChildren: false,
ParentFolder:{
ID: qParentFolderResponse[0].ID
},
Client:{
ID: req.session.fuel.mid
}
},
options: {}
};
etHelper.create(qnewFolderParms, SoapClient, function(err, qnewFolderResponse){
//console.log(qnewFolderResponse);
if(qnewFolderResponse.length > 0){
qDone = true;
response.QueryFolderID = qnewFolderResponse[0].NewID;
response.Status = true;
if(deDone)
{
callback(response);
}
}
else{
response.Status = false;
callback(response);
}
});
}
else
{
response.Status = false;
callback(response);
}
});
}
else
{
qDone = true;
response.QueryFolderID = qFolderResponse[0].ID;
response.Status = true;
if(deDone)
{
callback(response);
}
}
});
};
是的,至少有 100 种不同的方法可以解决这个问题,其中许多不一定比其他任何方法都好——这取决于您和您的团队最喜欢什么以及什么最适合您的需求。你真的只能通过经验来判断,同样的解决方案不会每次都是最适合你的。
我建议阅读 callbackhell.com, a manifesto on mitigating callback hell / pyramid of doom 继续阅读 没有 像 async
.
这样的外部库
在您的情况下,使用 async.waterfall
会奏效。它与 async.series
类似,只是它将非错误参数传递给系列中的下一个函数。
async.waterfall([
function(next){
etHelper.folder_find("dataextension", name, next);
},function(result, next){
if (null == result.ID) {
// stop everything
return next("some error");
}
etHelper.folder_find("dataextension", "Data Extensions", next);
}
], function (err, results) {
// handle `err` here if it's not null
});
还要注意 Promises, which are another construct for handling synchronization. There are several node promise libraries out there like bluebird
。
您的上述代码将以类似的方式工作,除了 folder_find
接受回调参数,它应该 return 一个您在内部拒绝以防出错的承诺。
etHelper.folder_find("dataextension", name)
.then(function (result) {
if (null == result.ID) {
throw new Error("some error");
}
return etHelper.folder_find("dataextension", "Data Extensions");
})
.then(function (result) {
console.log(result);
})
.catch(function (err) {
// handle error here
});
我是 node 的新手,最近阅读了大量关于如何最好地避免回调地狱的书。由于我的新手能力,我在我的代码中积累了一些金字塔,所以我点击 google 看看如何避免它。
我正在使用 Salesforce Marketing Cloud 构建应用程序 API...
这是我的场景。
- 检查我的帐户中是否存在特定文件夹。
- 如果存在 return 文件夹的 ID
- 如果它不存在,搜索父文件夹并获取它的 ID
- 创建文件夹并使用 ParentFolders ID 作为 属性。
所以在旧学校代码中,这非常简单,只需按顺序执行步骤 1-4。
在节点中,我最终得到了一个带有回调函数的函数。
感谢 Google 我遇到了异步库,但不太确定如何最好地利用它。
这是我的一段代码,它是我从运行服务器的 main.js 文件调用的更大模块的一部分
MyHelper.prototype.test = function(name, req, callback) {
async.series([
function(next){
etHelper.folder_find("dataextension", name, next);
},function(next){
etHelper.folder_find("dataextension", "Data Extensions", next);
}
],
function(err, results){
console.log(results)
});
};
我应该使用异步的瀑布方法吗?如果是这样,我将如何检查步骤 1 中的 ID,如果它不是 null exit
不确定它是否有帮助,但这是我从我的 ETHelper 方法构建的响应
[ { Success: false,
Status: 'Warning',
Message: 'Folder Not Found',
Type: 'dataextension',
Name: '10513542_SQLHelper_Test',
ID: null },
{ Success: true,
Status: 'OK',
Message: 'Folder Found',
Type: 'dataextension',
Name: 'Data Extensions',
ID: '1040721' } ]
我真的很想在这里学习,但我对所遇到的信息有点不知所措。似乎有 100 种不同的做事方式,很难知道选择哪一种。
最后,我不想搞砸这个 post 但这是我想出的原始方法来处理这个,我知道这是一团糟。
SQLHelper.prototype.checkInstall = function(name, req, callback) {
var response = new Object();
var deDone = false;
var qDone = false;
/*
*********************************************************************************************************
*
* This function will look for the SQLHelper folders. If they are not found they will be added
*
*********************************************************************************************************
*/
//Setup the DEFolder Lookup Filter
var deFolderParms = {
objectType: "DataFolder",
props: ["Name", "ID", "ContentType"],
filter: {
leftOperand: {
leftOperand: 'Name',
operator: 'equals',
rightOperand: name
},
operator: 'AND',
rightOperand:
{
leftOperand: 'ContentType',
operator: 'equals',
rightOperand: "dataextension"
}
}
};
//Look for the Folder
etHelper.getByName(deFolderParms, SoapClient, function(err, deFolderResponse){
//console.log(deFolderResponse);
if(deFolderResponse.length <=0)
{
//Folder Does not exist so create it
//But first we need the Parent Folder
var deParentFolderParms = {
objectType: "DataFolder",
props: ["Name", "ID", "ContentType"],
filter: {
leftOperand: {
leftOperand: 'Name',
operator: 'equals',
rightOperand: "Data Extensions"
},
operator: 'AND',
rightOperand:
{
leftOperand: 'ContentType',
operator: 'equals',
rightOperand: "dataextension"
}
}
};
etHelper.getByName(deParentFolderParms, SoapClient, function(err, deParentFolderResponse){
//console.log(deParentFolderResponse);
if(deParentFolderResponse.length > 0){
var newFolderParms = {
objectType: "DataFolder",
props: {
Name: name,
CustomerKey: name,
Description: "This folder is only to be used by the SQL Helper Application",
ContentType: "dataextension",
IsActive: true,
IsEditable: true,
AllowChildren: false,
ParentFolder:{
ID: deParentFolderResponse[0].ID
},
Client:{
ID: req.session.fuel.mid
}
},
options: {}
};
etHelper.create(newFolderParms, SoapClient, function(err, newFolderResponse){
//console.log(newFolderResponse);
if(newFolderResponse.length > 0){
deDone = true;
response.DEFolderID = newFolderResponse[0].NewID;
response.Status = true;
if(qDone)
{
callback(response);
}
}
else{
response.Status = false;
callback(response);
}
});
}
else
{
response.Status = false;
callback(response);
}
});
}
else
{
deDone = true;
response.DEFolderID = deFolderResponse[0].ID;
response.Status = true;
if(qDone)
{
callback(response);
}
}
});
//Setup the DEFolder Lookup Filter
var qFolderParms = {
objectType: "DataFolder",
props: ["Name", "ID", "ContentType"],
filter: {
leftOperand: {
leftOperand: 'Name',
operator: 'equals',
rightOperand: name
},
operator: 'AND',
rightOperand:
{
leftOperand: 'ContentType',
operator: 'equals',
rightOperand: "queryactivity"
}
}
};
//Look for the Folder
etHelper.getByName(qFolderParms, SoapClient, function(err, qFolderResponse){
//console.log(qFolderResponse);
if(qFolderResponse.length <=0)
{
//Folder Does not exist so create it
//But first we need the Parent Folder
var qParentFolderParms = {
objectType: "DataFolder",
props: ["Name", "ID", "ContentType"],
filter: {
leftOperand: {
leftOperand: 'Name',
operator: 'equals',
rightOperand: "Query"
},
operator: 'AND',
rightOperand:
{
leftOperand: 'ContentType',
operator: 'equals',
rightOperand: "queryactivity"
}
}
};
etHelper.getByName(qParentFolderParms, SoapClient, function(err, qParentFolderResponse){
//console.log(qParentFolderResponse);
if(qParentFolderResponse.length > 0){
var qnewFolderParms = {
objectType: "DataFolder",
props: {
Name: name,
CustomerKey: name,
Description: "This folder is only to be used by the SQL Helper Application",
ContentType: "queryactivity",
IsActive: true,
IsEditable: true,
AllowChildren: false,
ParentFolder:{
ID: qParentFolderResponse[0].ID
},
Client:{
ID: req.session.fuel.mid
}
},
options: {}
};
etHelper.create(qnewFolderParms, SoapClient, function(err, qnewFolderResponse){
//console.log(qnewFolderResponse);
if(qnewFolderResponse.length > 0){
qDone = true;
response.QueryFolderID = qnewFolderResponse[0].NewID;
response.Status = true;
if(deDone)
{
callback(response);
}
}
else{
response.Status = false;
callback(response);
}
});
}
else
{
response.Status = false;
callback(response);
}
});
}
else
{
qDone = true;
response.QueryFolderID = qFolderResponse[0].ID;
response.Status = true;
if(deDone)
{
callback(response);
}
}
});
};
是的,至少有 100 种不同的方法可以解决这个问题,其中许多不一定比其他任何方法都好——这取决于您和您的团队最喜欢什么以及什么最适合您的需求。你真的只能通过经验来判断,同样的解决方案不会每次都是最适合你的。
我建议阅读 callbackhell.com, a manifesto on mitigating callback hell / pyramid of doom 继续阅读 没有 像 async
.
在您的情况下,使用 async.waterfall
会奏效。它与 async.series
类似,只是它将非错误参数传递给系列中的下一个函数。
async.waterfall([
function(next){
etHelper.folder_find("dataextension", name, next);
},function(result, next){
if (null == result.ID) {
// stop everything
return next("some error");
}
etHelper.folder_find("dataextension", "Data Extensions", next);
}
], function (err, results) {
// handle `err` here if it's not null
});
还要注意 Promises, which are another construct for handling synchronization. There are several node promise libraries out there like bluebird
。
您的上述代码将以类似的方式工作,除了 folder_find
接受回调参数,它应该 return 一个您在内部拒绝以防出错的承诺。
etHelper.folder_find("dataextension", name)
.then(function (result) {
if (null == result.ID) {
throw new Error("some error");
}
return etHelper.folder_find("dataextension", "Data Extensions");
})
.then(function (result) {
console.log(result);
})
.catch(function (err) {
// handle error here
});