互联网科技不羁放纵爱前端让前端飞

ES6 Promise 对象

2018-11-18  本文已影响2人  淘淘笙悦

在 MDN 中对 Promise 的定义是:Promise 对象用于表示一个异步操作的最终状态(完成或失败),以及其返回的值。

Promise 对象存在以下三种状态:

Promise 对象的初始状态是 pending,最终状态是 fulfilled 或者 rejected,其中 fulfilled 表示成功状态,rejected 表示失败状态。状态只能够由 pending 变成 fulfilled 或 rejected,当 Promise 对象的状态改变后就不可以再改变了。

Promise 状态改变

Promise 简单的使用方式如下

let pro = new Promise((resolve, reject) => {
  // 执行异步操作代码
  // 异步操作成功,则 resolve(value)
  // 异步操作失败,则 reject(error)
});

从上面的使用方式来看,我们可以知道 Promise 是一个构造函数,其参数接受一个匿名函数,该匿名函数有两个参数,分别是 resolve 和 reject 函数。

通过 new Promise 得到一个 Promise 对象,Promise 构造函数所接受的匿名函数会直接执行,一般是在里面执行异步操作。

当异步操作成功的时候,执行 resolve 函数,会把 Promise 对象的状态由 pending 变为 fulfilled;当异步操作失败的时候,执行 reject 函数,会把 Promise 对象的状态由 pending 变为 rejected。其中 resolve 和 reject 函数都分别传入了返回值(value)和错误值(error),这两个值将会作为 Promise 对象 then 方法的两个回调函数的实参。

Promise 对象上具有很多方法,其方法是挂载在对应的原型即 Promise.prototype 上面的,下面就 Promise 对象的方法做一个详细的介绍。

Promise.prototype.then(onFulfilled, onRejected)

当 Promise 对象的状态由 pending 变为 fulfilled 或 rejected 的时候,会触发 Promise 对象的 then 方法。

pro.then((value) => {
  // fulfilled
}, (error) => {
  // rejected
});

then 方法接受两个回调函数作为参数,当 Promise 对象当状态由 pending 变为 fulfilled 的时候,执行第一个回调函数;当 Promise 对象当状态由 pending 变为 rejected 的时候,执行第二个回调函数(可选)。这两个回调函数的参数分别是执行 resolve 和 reject 函数时传入的值。

举个简单的例子

let timeoutPro = new Promise((resolve, reject) => {
  setTimeout(resolve, 1000, 'resolve');
});
timeoutPro.then((value) => {
  console.log(value);
});

我们还可以将其封装起来

function timeout(ms) {
  return new Promise((resolve, reject) => {
    setTimeout(resolve, ms, 'resolve');
  });
}
timeout(1000).then((value) => {
  console.log(value);
});

下面是一个用 Promise对象实现的 Ajax 操作的例子

let get = function (url) {
  return new Promise((resolve, reject) => {
    let xhr = new XMLHttpRequest();
    xhr.open("GET", url);
    xhr.responseType = "json";
    xhr.setRequestHeader("Accept", "application/json");
    xhr.send();
    xhr.onreadystatechange = () => {
      if (xhr.readyState != 4) return;
      if (xhr.status == 200) {
        resolve(xhr.response);
      } else {
        reject(xhr.statusText);
      }
    };
  });
};
get("0010.json").then((res) => {
  console.log(res.name);  // ttsy
}, (error) => {
  console.log(error);
});

注:这里 0010.json 为一个与存放上述代码的脚本文件同目录的一个 json 文件,里面的内容如下

{
  "name":"ttsy",
  "age":25
}

then 方法返回的是一个新的 Promise 对象。因此可以采用链式写法,即 then 方法后面再调用另一个 then 方法。而下一个 then 方法的行为跟前一个 then 方法的返回值有关

举个例子~

当上一个 then 中的回调函数返回一个值时

timeout(1000).then((value) => {
  return value;
}).then((value) => {
  console.log(value);  // resolve
})

当上一个 then 中的回调函数抛出一个错误时

timeout(1000).then((value) => {
  throw 'error'
}).then((value) => {
  console.log(value);
}, (error) => {
  console.log(error)  // error
})

当上一个 then 中的回调函数返回一个 Promise 对象时

timeout(1000).then((value) => {
  return timeout(1000);
}).then((value) => {
  console.log(value);  // resolve
}, (error) => {
  console.log(error)
})

一般来讲,在 then 中的回调函数返回一个 Promise 对象的场景都是 http 请求的嵌套,上述例子只是为了更方便的表示这种情况,实际场景中并不会去这么使用。

上面说的是下一个 then 方法的行为跟前一个 then 方法的返回值有关,但如果一开始创建的 Promise 对象的状态由 pending 变为 rejected 时,又是怎样的情况呢?

function timeoutReject(ms) {
  return new Promise((resolve, reject) => {
    setTimeout(reject, ms, 'reject');
  });
}
timeoutReject(1000).then((value) => {
  console.log(value);
}).then(undefined, (error) => {
  console.log(error)  // error
})

可以看到, Promise 对象的状态变为失败状态时执行第二个 then 中的第二个回调函数的代码。实际上,Promise 对象的错误具有「冒泡」性质,会一直向后传递,直到被捕获为止。

Promise.prototype.catch(onRejected)

Promise.prototype.catch(onRejected) 与 Promise.prototype.then(undefined, onRejected) 是一样的效果,用于指定发生错误时的回调函数。同样的,catch 也返回一个新的 Promise 对象。

timeoutReject(1000).then((value) => {
  console.log(value);
}).catch((error) => {
  console.log(error)  // error
})

而在上面说到,Promise 对象的错误具有「冒泡」性质,会一直向后传递,直到被捕获为止。所以当前面的 Promise 对象抛出错误时,则会依次冒泡,直到被 catch 捕获。

timeoutReject(1000).then((value) => {
  console.log(value);
}).then((value) => {
  console.log(value);
}).catch((error) => {
  console.log(error)  // error
})

上述代码中,catch 会捕获 timeoutReject() 和两个 then 所返回的 Promise 对象所抛出的错误。

一般来说,不要在 then 方法里面定义 Reject 状态的回调函数(即 then 的第二个参数),总是使用 catch 方法。

Promise.all()

Promise.all 方法用于将多个 Promise 对象合成一个 Promise 对象,它接受一个数组作为参数,数组中的每一个元素都是 Promise 对象。最终返回一个新的 Promise 对象,其状态由传入的 Promise 对象共同决定,分为以下两种情况。

传入的 Promise 对象的状态都变成 fulfilled 时

function timeout1(ms) {
  return new Promise((resolve, reject) => {
    setTimeout(resolve, ms, 'resolve');
  });
}
function timeout2(ms) {
  return new Promise((resolve, reject) => {
    setTimeout(resolve, ms, 'resolve');
  });
}
Promise.all([timeout1(1000), timeout2(2000)]).then((value) => {
  console.log(value); // [ 'resolve', 'resolve' ]
}).catch((error) => {
  console.log(error)  
})

当传入的 Promise 对象的状态其中有一个变成 rejected 时

function timeout(ms) {
  return new Promise((resolve, reject) => {
    setTimeout(resolve, ms, 'resolve');
  });
}
function timeoutReject(ms) {
  return new Promise((resolve, reject) => {
    setTimeout(reject, ms, 'reject');
  });
}
Promise.all([timeout(1000), timeoutReject(2000)]).then((value) => {
  console.log(value); 
}).catch((error) => {
  console.log(error)  // reject
})
上一篇 下一篇

猜你喜欢

热点阅读