Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/node.js/41.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
避免javascript回调和承诺地狱_Javascript_Node.js_Promise - Fatal编程技术网

避免javascript回调和承诺地狱

避免javascript回调和承诺地狱,javascript,node.js,promise,Javascript,Node.js,Promise,我有许多异步方法要执行,我的程序流可以根据每个方法的返回而发生很大的变化。下面的逻辑就是一个例子。我不能用一种简单易读的方式用承诺来写。你会怎么写 Ps:欢迎更复杂的流程 Ps2:is_business是一个预定义的标志,在这里我们可以说是编写业务用户还是个人用户 begin transaction update users if updated if is_business update_business if not updated

我有许多异步方法要执行,我的程序流可以根据每个方法的返回而发生很大的变化。下面的逻辑就是一个例子。我不能用一种简单易读的方式用承诺来写。你会怎么写

Ps:欢迎更复杂的流程

Ps2:is_business是一个预定义的标志,在这里我们可以说是编写业务用户还是个人用户

begin transaction
update users
if updated
    if is_business
        update_business
        if not updated
            insert business
        end if
    else
        delete business
    end if
else
    if upsert
        insert user
        if is_business
            insert business
        end if
    end if
end if
commit transaction

承诺的好处在于,它们在同步代码和异步代码之间做了一个简单的类比。要说明如何使用Q库,请执行以下操作:

同步:

var thisReturnsAValue = function() {
  var result = mySynchronousFunction();
  if(result) {
    return getOneValue();
  } else {
    return getAnotherValue();
  }
};

try {
  var value = thisReturnsAValue();
  console.log(value);
} catch(err) {
  console.error(err);
}
var Q = require('q');

var thisReturnsAPromiseForAValue = function() {
  return Q.Promise(function() {
    return myAsynchronousFunction().then(function(result) {
      if(result) {
        // Even getOneValue() would work here, because a non-promise
        // value is automatically cast to a pre-resolved promise
        return getOneValueAsynchronously();
      } else {
        return getAnotherValueAsynchronously();
      }
    });
  });
};

thisReturnsAPromiseForAValue().then(function(value) {
  console.log(value);
}, function(err) {
  console.error(err);
});
异步:

var thisReturnsAValue = function() {
  var result = mySynchronousFunction();
  if(result) {
    return getOneValue();
  } else {
    return getAnotherValue();
  }
};

try {
  var value = thisReturnsAValue();
  console.log(value);
} catch(err) {
  console.error(err);
}
var Q = require('q');

var thisReturnsAPromiseForAValue = function() {
  return Q.Promise(function() {
    return myAsynchronousFunction().then(function(result) {
      if(result) {
        // Even getOneValue() would work here, because a non-promise
        // value is automatically cast to a pre-resolved promise
        return getOneValueAsynchronously();
      } else {
        return getAnotherValueAsynchronously();
      }
    });
  });
};

thisReturnsAPromiseForAValue().then(function(value) {
  console.log(value);
}, function(err) {
  console.error(err);
});
您只需要习惯返回值总是作为参数访问然后回调的想法,并且链接承诺等同于组成函数调用fghx或按顺序var x2=hx执行函数;变量x3=gx2;。基本上就是这样!当您引入分支时,事情会变得有点棘手,但是您可以从这些第一原则中找出要做的事情。因为回调接受承诺作为返回值,所以您可以通过为异步操作返回另一个承诺来改变异步获得的值,该异步操作将在旧值的基础上解析为新值,并且父承诺在新值解析之前不会解析!当然,你也可以从if-else分支内部回报这些承诺

上面示例中说明的另一件非常好的事情是,至少承诺那些符合承诺/A+的人以同样类似的方式处理异常。引发的第一个错误会绕过非错误回调,并冒泡到第一个可用的错误回调,就像try-catch块一样

值得一提的是,我认为尝试使用手工制作的Node.js风格回调和异步库来模拟这种行为是一种特殊的地狱:

遵循这些准则,您的代码将成为假设所有函数都是异步的并返回承诺:

beginTransaction().then(function() {
  // beginTransaction() has run
  return updateUsers(); // resolves the boolean value `updated`
}).then(function(updated) {
  // updateUsers() has "returned" `updated`
  if(updated) {
    if(isBusiness) {
      return updateBusiness().then(function(updated) {
        if(!updated) {
          return insertBusiness();
        }
        // It's okay if we don't return anything -- it will
        // result in a promise which immediately resolves to
        // `undefined`, which is a no-op, just like a missing
        // else-branch
      });
    } else {
      return deleteBusiness();
    }
  } else {
    if(upsert) {
      return insertUser().then(function() {
        if(isBusiness) {
          return insertBusiness();
        }
      });
    }
  }
}).then(function() {
  return commitTransaction();
}).done(function() {
  console.log('all done!');
}, function(err) {
  console.error(err);
});

承诺的好处在于,它们在同步代码和异步代码之间做了一个简单的类比。要说明如何使用Q库,请执行以下操作:

同步:

var thisReturnsAValue = function() {
  var result = mySynchronousFunction();
  if(result) {
    return getOneValue();
  } else {
    return getAnotherValue();
  }
};

try {
  var value = thisReturnsAValue();
  console.log(value);
} catch(err) {
  console.error(err);
}
var Q = require('q');

var thisReturnsAPromiseForAValue = function() {
  return Q.Promise(function() {
    return myAsynchronousFunction().then(function(result) {
      if(result) {
        // Even getOneValue() would work here, because a non-promise
        // value is automatically cast to a pre-resolved promise
        return getOneValueAsynchronously();
      } else {
        return getAnotherValueAsynchronously();
      }
    });
  });
};

thisReturnsAPromiseForAValue().then(function(value) {
  console.log(value);
}, function(err) {
  console.error(err);
});
异步:

var thisReturnsAValue = function() {
  var result = mySynchronousFunction();
  if(result) {
    return getOneValue();
  } else {
    return getAnotherValue();
  }
};

try {
  var value = thisReturnsAValue();
  console.log(value);
} catch(err) {
  console.error(err);
}
var Q = require('q');

var thisReturnsAPromiseForAValue = function() {
  return Q.Promise(function() {
    return myAsynchronousFunction().then(function(result) {
      if(result) {
        // Even getOneValue() would work here, because a non-promise
        // value is automatically cast to a pre-resolved promise
        return getOneValueAsynchronously();
      } else {
        return getAnotherValueAsynchronously();
      }
    });
  });
};

thisReturnsAPromiseForAValue().then(function(value) {
  console.log(value);
}, function(err) {
  console.error(err);
});
您只需要习惯返回值总是作为参数访问然后回调的想法,并且链接承诺等同于组成函数调用fghx或按顺序var x2=hx执行函数;变量x3=gx2;。基本上就是这样!当您引入分支时,事情会变得有点棘手,但是您可以从这些第一原则中找出要做的事情。因为回调接受承诺作为返回值,所以您可以通过为异步操作返回另一个承诺来改变异步获得的值,该异步操作将在旧值的基础上解析为新值,并且父承诺在新值解析之前不会解析!当然,你也可以从if-else分支内部回报这些承诺

上面示例中说明的另一件非常好的事情是,至少承诺那些符合承诺/A+的人以同样类似的方式处理异常。引发的第一个错误会绕过非错误回调,并冒泡到第一个可用的错误回调,就像try-catch块一样

值得一提的是,我认为尝试使用手工制作的Node.js风格回调和异步库来模拟这种行为是一种特殊的地狱:

遵循这些准则,您的代码将成为假设所有函数都是异步的并返回承诺:

beginTransaction().then(function() {
  // beginTransaction() has run
  return updateUsers(); // resolves the boolean value `updated`
}).then(function(updated) {
  // updateUsers() has "returned" `updated`
  if(updated) {
    if(isBusiness) {
      return updateBusiness().then(function(updated) {
        if(!updated) {
          return insertBusiness();
        }
        // It's okay if we don't return anything -- it will
        // result in a promise which immediately resolves to
        // `undefined`, which is a no-op, just like a missing
        // else-branch
      });
    } else {
      return deleteBusiness();
    }
  } else {
    if(upsert) {
      return insertUser().then(function() {
        if(isBusiness) {
          return insertBusiness();
        }
      });
    }
  }
}).then(function() {
  return commitTransaction();
}).done(function() {
  console.log('all done!');
}, function(err) {
  console.error(err);
});

解决方案是@mooiamaduck-answer和@Kevin-comment的混合

使用promises、ES6生成器和库可以使代码更加清晰。在阅读postgresql节点库示例时,我发现了一个很好的示例。在下面的示例中,pool.connect和client.query是返回承诺的异步操作。我们可以在获得结果后轻松添加if/else,然后进行更多异步操作,使代码看起来像同步的

co(function * () {
  var client = yield pool.connect()
  try {
      yield client.query('BEGIN')
      var result = yield client.query('SELECT $1::text as name', ['foo'])
      yield client.query('INSERT INTO something(name) VALUES($1)', [result.rows[0].name])
      yield client.query('COMMIT')
      client.release()
  } catch(e) {
    // pass truthy value to release to destroy the client
    // instead of returning it to the pool
    // the pool will create a new client next time
    // this will also roll back the transaction within postgres
    client.release(true)
  }
})

解决方案是@mooiamaduck-answer和@Kevin-comment的混合

使用promises、ES6生成器和库可以使代码更加清晰。在阅读postgresql节点库示例时,我发现了一个很好的示例。在下面的示例中,pool.connect和client.query是返回承诺的异步操作。我们可以在获得结果后轻松添加if/else,然后进行更多异步操作,使代码看起来像同步的

co(function * () {
  var client = yield pool.connect()
  try {
      yield client.query('BEGIN')
      var result = yield client.query('SELECT $1::text as name', ['foo'])
      yield client.query('INSERT INTO something(name) VALUES($1)', [result.rows[0].name])
      yield client.query('COMMIT')
      client.release()
  } catch(e) {
    // pass truthy value to release to destroy the client
    // instead of returning it to the pool
    // the pool will create a new client next time
    // this will also roll back the transaction within postgres
    client.release(true)
  }
})

我认为这是一个公平的问题。所有这些操作都是异步的吗?是的@mooiamaduck。实际上,我正在做的数据库插入/更新都是异步的。逻辑有问题。如果是业务{*..*}否则{*删除业务*}。如果不是业务,为什么要删除业务。同样,*更新业务*如果未更新*插入业务*。您应该更新业务或插入业务。您不需要使用任何库。在ES7引入异步和wait之前,您可以实现您的
使用标准ES6 promises/generator实现Promise.coroutine方法,并在生成器函数中处理异步工作流,就像它是同步的一样。如果今天晚些时候我能抽出时间,我将尝试给出一个例子。@Redu理解它的工作原理是好的,但我不确定为什么我会自己实现它,只是说我做到了,当有诸如bluebird's Promise.coroutine或co library这样的选项可用时。我认为这是一个公平的问题。所有这些操作都是异步的吗?是的@mooiamaduck。实际上,我正在做的数据库插入/更新都是异步的。逻辑有问题。如果是业务{*..*}否则{*删除业务*}。如果不是业务,为什么要删除业务。同样,*更新业务*如果未更新*插入业务*。您应该更新业务或插入业务。您不需要使用任何库。在ES7引入relieving async and Wait之前,您可以使用标准ES6 promises/generator实现简单的Promise.corroutine方法,并在generator函数中处理异步工作流,就像它是同步的一样。如果今天晚些时候我能抽出时间,我将尝试给出一个例子。@Redu了解它的工作原理很好,但我不确定为什么我会自己实现它,只是说我做到了,当有诸如bluebird's Promise.coroutine或co library可用的选项时。非常感谢@mooiamaduck。这正是我所期望的。我已经了解它是如何工作的,但不幸的是,在我看来,它仍然很难阅读。我发现创建一个回调函数并在开关/案例中使用标志更容易理解。我觉得这更自然,所以我认为这是一个品味问题。让我害怕的是,非承诺代码需要转换为承诺代码才能与其他承诺代码一起使用。这意味着,一旦你开始使用一些承诺,它们就会淹没你的整个代码库,直到你写的几乎所有东西都是基于承诺的。这并不一定是坏事,但如果你遇到了一条死胡同,代码由于某种原因无法基于承诺,那么你会害怕会发生什么。也许这永远不会发生?非常感谢@mooiamaduck。这正是我所期望的。我已经了解它是如何工作的,但不幸的是,在我看来,它仍然很难阅读。我发现创建一个回调函数并在开关/案例中使用标志更容易理解。我觉得这更自然,所以我认为这是一个品味问题。让我害怕的是,非承诺代码需要转换为承诺代码才能与其他承诺代码一起使用。这意味着,一旦你开始使用一些承诺,它们就会淹没你的整个代码库,直到你写的几乎所有东西都是基于承诺的。这并不一定是坏事,但如果你遇到了一条死胡同,代码由于某种原因无法基于承诺,那么你会害怕会发生什么。也许这永远不会发生?