九、Promises

107 阅读3分钟

异步回调

在异步编程中,我们一般都是使用回调函数,我们将一个函数传递给一个异步函数,当任务完成时它将调用这个函数。

function doAsyncTask(cb) {
  setTimeout(() => {
    console.log("Async Task Calling Callback");
    cb();
  }, 1000);
}

doAsyncTask(() => console.log("Callback Called"));

以上例子应该非常熟悉了,在开发中经常应用到,doAsyncTask函数在调用异步的时候,就会调用我们的cb 函数,这种就是回调函数,cb其实是个简写,因为叫做callback,所有一般都这样写

Promise API

在es6中,给我们带来了个回调的替代方案promise,其实这个promise在es5的时候就有很多第三方的实现库,最早的时候,在jq就有类似的实现了;
它的功能与回调函数相同,但语法更好,更容易处理错误

创建一个Promise对象

我们通过调用promise类的new来创建一个promise的实例

var promise = new Promise((resolve, reject) => {
});

我们传递给Promise一个内置函数,并且它接受两个内置参数(resolve, reject),我们可以定义了这个函数,里面的两个参数可以随便我们叫什么,但是我们一般通过语义化的叫法,一般都叫做resolve和reject
==resolve和reject其实也是函数体的一部分==
在这个函数中调用异步函数,我们就可以调用resolve(),如下:

var promise = new Promise((resolve, reject) => {
  setTimeout(() => {
    console.log("Async Work Complete");
    resolve();
  }, 1000);
});

一般我们返回这个promise实例在一个函数中,如下:

function doAsyncTask() {
  var promise = new Promise((resolve, reject) => {
    setTimeout(() => {
      console.log("Async Work Complete");
      resolve();
    }, 1000);
  });
  return promise;
}

如果函数中有报错,将执行reject(),以下代码未完善,error只是表示个意思

function doAsyncTask() {
  var promise = new Promise((resolve, reject) => {
    setTimeout(() => {
      console.log("Async Work Complete");
      if (error) {
        reject();
      } else {
        resolve();
      }
    }, 1000);
  });
  return promise;
}

我们可以将一个成功的函数附加到then函数中处理,比如:

doAsyncTask().then(() => console.log("Task Complete!"));

then 函数中可以带两个参数,第一个以上代码所示,是成功的回调,而还有一个参数是失败的回调函数,可以写成以下方式:

doAsyncTask().then(
  () => console.log("Task Complete!"),
  () => console.log("Task Errored!"),
);

传递给resolve和reject函数的任何值都会传递给成功和错误处理程序

let error = true;
function doAsyncTask() {
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      if (error) {
        reject('error'); // pass values
      } else {
        resolve('done'); // pass values
      }
    }, 1000);
  });
}

doAsyncTask().then(
  (val) => console.log(val),
  (err) => console.error(err)
);

挂载到Promise的resolve和reject

我们可以使用Promise.resolve()方法,直接声明成功回调的方法

let promise = Promise.resolve('done');

同样的也有Promise.reject()方法

let promise = Promise.reject('fail');

接着也可以和正常声明一样,使用then方法执行回调

let promise = Promise.resolve('done');
promise.then((val) => console.log(val)); // 'done'

链式调用

我们可以用then链接做链式调用,因为promise对象每执行完都会返回一个promise对象

Promise.resolve("done")
  .then(
    (val) => {
      console.log(val);
      return 'done2';
    },
    (err) => console.error(err)
  )
  .then(
    (val) => console.log(val),
    (err) => console.error(err)
  );
// 'done'
// 'done2'

Promise的错误将会在链式中传递,所以我们不需要在每个then中处理错误,只需要在最后一个中调用reject就可以

Promise.reject('fail')
  .then((val) => console.log(val))
  .then(
    (val) => console.log(val),
    (err) => console.error(err)
  );

如果在成功的回调中有报错,那么promise对象将会捕获错误在reject中

Promise.resolve('done')
  .then((val) => {
    throw new Error("fail")
  })
  .then(
    (val) => console.log(val),
    (err) => console.error(err)
  );
// [Error: fail]

catch捕获异常

catch函数是用来捕获异常的函数,这样可以更清晰明确的描述错误

Promise.resolve('done')
  .then((val) => {throw new Error("fail")})
  .then((val) => console.log(val))
  .catch((err) => console.error(err));

总结

Promise能更清晰优雅的写异步回调的代码,捕获异常方面的代码也更加的清晰集中