如何在 ES6 中将回调代码转换为 Promise [重复]

Posted

技术标签:

【中文标题】如何在 ES6 中将回调代码转换为 Promise [重复]【英文标题】:How to convert a callback code to promise in ES6 [duplicate] 【发布时间】:2016-12-20 17:12:51 【问题描述】:

我正在学习 ES6 标准,所以我从一个非常基本的示例代码开始。

javascript 中存在回调地狱,所以这次我确实想避免使用回调。但是我遇到了一个问题,我真的不知道如何将回调样式代码转换为promise。

例如,如果我有这样的代码,如下所示

module.exports = (x, y, callback) => 
  try 
    if (x < 0 || y < 0) 
      throw new Error('Rectangle dimensions are wrong.');
     else 
      callback(null, 
        perimeter() 
          return (2 * (x + y));
        ,
        area() 
          return (x * y);
        ,
      );
    
   catch (error) 
    callback(error, null);
  
;

我应该如何在 ES6 中将其转换为 Promise?这是一种将回调转换为承诺的推荐行为吗?

我已阅读此示例,但实际上我对结果感到困惑。我认为在我开始将回调重写为 Promise 之前,我需要先了解这一点。

let promise = new Promise(function(resolve, reject) 
  console.log('Promise');
  resolve();
);

promise.then(function() 
  console.log('Resolved.');
);

console.log('Hi!');

// Promise
// Hi!
// Resolved 

我的理解是Promise 在创建后立即运行。但是我不知道为什么then方法中的代码会最后运行。

【问题讨论】:

如果您阅读 Promise/A+ here 的规范,请特别注意 2.2.4 和相关的注释 3.1 - 基本上,.then 回调是异步的 【参考方案1】:
module.exports = (x, y, callback) => 
  new Promise(function(resolve, reject) 
    if (x < 0 || y < 0) reject(new Error('Rectangle dimensions are wrong.'))
    else resolve(
      perimeter() 
        return (2 * (x + y));
      ,
      area() 
        return (x * y);
      
    )
  )
  .then(callback)
  .catch(callback)

记住“.then”和“.catch”是异步的。

【讨论】:

【参考方案2】:

Promise 非常好,但一开始可能会有点混乱,请检查以下代码:

module.exports = (x, y) => 
var deferred = q.defer();// this will be our promise
  try 
    if (x < 0 || y < 0) 
      //return an error to be catched by catch method
      deferred.reject(new Error('Rectangle dimensions are wrong.'));
     else 
      //return the event to the next function with this value
      deferred.resolve(
        perimeter() 
          return (2 * (x + y));
        ,
        area() 
          return (x * y);
        ,
      );
    
   catch (error) 
    deferred.reject(error);
  

 return deferred.promise; //Here return the promise
;

//You will use it like this
module(x,y)
.then(callback)
.then(function(data)
 )
.catch(function(error)
);

在我的例子中,当你调用你的模块时,你会立即得到 promise,但是代码还没有被执行,在代码被执行之后,你会在你的“then”方法中得到事件,或者如果在你的收获。

我真的很喜欢 q 库来处理 Promise,让您可以控制如何返回错误并在发生错误时停止链。基本上,您可以更好地控制功能流。

希望对你有帮助

【讨论】:

【参考方案3】:

这是一个重要的话题。为了以同步的方式进行功能编码,使用 Promise 的目的是将callback 中的逻辑移动到then 阶段。因此,尽管您可以不处理承诺本身的逻辑,但应在then 阶段处理。这种思维方式有助于我们创建一个通用的 promisify 实用函数,它适用于所有特定类型的回调结构。在您的情况下,回调类型是 Node 标准错误第一类型。所以根据你下面的代码

module.exports = (x, y, callback) => 
  try 
    if (x < 0 || y < 0) 
      throw new Error('Rectangle dimensions are wrong.');
     else 
      callback(null, 
        perimeter() 
          return (2 * (x + y));
        ,
        area() 
          return (x * y);
        ,
      );
    
   catch (error) 
    callback(error, null);
  
;

一个通用的promisfy函数应该如下工作;

var moduleExports = (x, y, callback) => 
  try 
    if (x < 0 || y < 0) 
      throw new Error('Rectangle dimensions are wrong.');
     else 
      callback(null, 
        perimeter() 
          return (2 * (x + y));
        ,
        area() 
          return (x * y);
        ,
      );
    
   catch (error) 
    callback(error, null);
  
;

function promisfy(fun, ...args)
  return new Promise((v,x) => fun(...args, (err,data) => !!err ? x(err) : v(data)));


var p = promisfy(moduleExports,4,5);
p.then(val => console.log(val,val.area(),val.perimeter()), err => console.log(err));

// p.then(val => callback(null,val), err => callback(err))

因此,在这种特殊情况下,您的结果对象将带有 areaperimeter 函数作为值参数提供给 then 阶段 onFulfillment。因此,您应该调用逻辑以在onFulfillment 函数中使用它们(then 阶段第一个回调)并处理onReject 函数中的任何错误(then 阶段第二个回调)。我在上面的 sn-p 中将其显示为最后一行的注释。

PS:我使用v(看起来像检查)来指定resolvex(很明显)来指定reject的promise回调。

【讨论】:

【参考方案4】:

现有答案属于deferred anti-pattern。我会避免这种方法,因为它不必要地冗长并且没有利用完整的 Promise API。另一个答案使用承诺。只有当您无法更改使用回调样式编写的代码时才需要使用 Promisification,例如使用第三方脚本。

您在问两个问题,第二个问题是为什么 Promises 的行为方式与您在给定示例中看到的方式相同。要找到这个问题的答案,我建议您使用许多现有的关于这种性质的 SO 的问题。例如aren't Promises just callbacks?

至于您的第一个问题,关于如何重构代码以使用 Promises,这是我的建议:

module.exports = (x, y) => 
  if (x < 0 || y < 0) 
    return Promise.reject(new Error('Rectangle dimensions are wrong.'));
   else 
    return Promise.resolve(
      perimeter() 
        return (2 * (x + y));
      ,
      area() 
        return (x * y);
      ,
    );
  
;

// e.g. success
createRectangle(10, 10)
  .then(rect => 
    console.log(rect.area()) //=> 100
  )

// e.g. failure
createRectangle(-1, -1)
  .catch(err => 
    console.log(err) //=> "Error: Rectangle dimensions are wrong."
  )

由于函数本身不依赖于异步操作的完成,我们可以使用辅助方法 Promise#resolvePromise#reject 从函数返回一个 Promise,表示创建“矩形”的成功或失败目的。这些会产生一个新的 Promise,其 status 会分别以值或错误来解决或拒绝。

【讨论】:

酷,这正是我想要的。我肯定会查看其他一些关于 Promise 的帖子。

以上是关于如何在 ES6 中将回调代码转换为 Promise [重复]的主要内容,如果未能解决你的问题,请参考以下文章

把 Node.js 中的回调转换为 Promise

如何在 node.js 中将函数与 Promise 同步

如何将现有的回调 API 转换为 Promise?

如何将现有的回调 API 转换为 Promise?

如何将现有的回调 API 转换为 Promise?

如何将现有的回调 API 转换为 Promise?