如何在ES6中将回调代码转换为promise

时间:2016-08-14 04:28:27

标签: javascript asynchronous callback promise ecmascript-6

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

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

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

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?这是一种将回调转换为承诺的推荐行为吗?

我已经阅读了这个例子,但实际上我对结果感到困惑。我想在开始重写我对回复的回调之前,我需要首先理解这一点。

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方法中的代码最后会运行。

4 个答案:

答案 0 :(得分:2)

现有答案成为deferred anti-pattern的牺牲品。我会避免这种方法,因为它不必要地冗长,并且没有利用完整的Promise API。另一个答案使用了promisification。当您无法更改使用回调样式编写的代码(例如使用第三方脚本)时,实际上只需要使用promisification。

你问两个问题,第二个问题是为什么Promises的行为与你在给定的例子中看到的方式相同。为了找到这个问题的答案,我建议你使用关于这种性质的许多现有问题。例如,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创建&#34;矩形&#34;宾语。这些会产生一个新的Promise,其 status 分别以值或错误被解析或拒绝。

答案 1 :(得分:0)

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)
}

记住&#34;。然后&#34;和&#34; .catch&#34;是异步的。

答案 2 :(得分:0)

这是一个重要的主题。为了以同步的方式进行功能编码,使用promises的目的是将callback中的逻辑移动到then阶段。因此,虽然您可以在then阶段处理承诺本身内部的逻辑。这种思维方式有助于我们创建一个通用的promisify实用程序函数,它适用于所有特定类型的回调结构。在您的情况下,回调类型是节点标准错误第一类型。所以按照下面的代码

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阶段第二次回调)中的任何错误。我在上一行中将其显示为上述代码段中的注释。

PS:我使用v(看起来像支票)来指定resolvex(显而易见)来指定承诺的reject回调。

答案 3 :(得分:-1)

承诺真的很好,但在开始时可能有点困惑,请查看此代码:

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){
});

在我的示例中,当您调用模块时,您将立即得到承诺,但代码尚未执行,代码执行后,您将在&#34;然后&#34;方法或者如果你的捕获中发生了什么。

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

希望对你有帮助