用法与封装


基本用法

  1. //实例化 Promise 对象
  2. const p = new Promise(function(resolve, reject){
  3. setTimeout(function(){
  4. // let data = '数据库中的用户数据';
  5. // resolve(data);
  6. let err = '数据读取失败';
  7. reject(err);
  8. }, 1000);
  9. });
  10. //调用 promise 对象的 then 方法
  11. p.then(function(value){
  12. console.log(value);
  13. }, function(reason){
  14. console.error(reason);
  15. })

封装 - 提取公用的读取多个文件的方法

  1. //先定义一个含promise的函数
  2. let myPromise (path) = {
  3. return new Promise((resolve,reject)=>{
  4. //读取文件
  5. fs.readFile(path,'utf-8',(err,data)=>{
  6. if(err) return reject(err)
  7. resolve(data)
  8. });
  9. });
  10. };
  11. //调用该函数 执行读取文件再.then 执行下一个
  12. myPromise('path')
  13. .then((data) => {
  14. console.log(data)
  15. return myPromise('path2')
  16. })
  17. .then((data2) => {
  18. console.log(data2)
  19. return myPromise('path3')
  20. })
  21. .cath((err)=>{ //如果有任何一个promise执行失败则返回
  22. console.log(err)
  23. })

封装 - Ajax

把成功与失败的回调交给外界,不用在onreadystatechange中处理。

  1. // 接口地址: https://api.apiopen.top/getJoke
  2. const p = new Promise((resolve, reject) => {
  3. //1. 创建对象
  4. const xhr = new XMLHttpRequest();
  5. //2. 初始化
  6. xhr.open("GET", "https://api.apiopen.top/getJoke");
  7. //3. 发送
  8. xhr.send();
  9. //4. 绑定事件, 处理响应结果
  10. xhr.onreadystatechange = function () {
  11. //判断
  12. if (xhr.readyState === 4) {
  13. //判断响应状态码 200-299
  14. if (xhr.status >= 200 && xhr.status < 300) {
  15. //表示成功
  16. resolve(xhr.response);
  17. } else {
  18. //如果失败
  19. reject(xhr.status);
  20. }
  21. }
  22. }
  23. })
  24. //指定回调
  25. p.then(function(value){
  26. console.log(value);
  27. }, function(reason){
  28. console.error(reason);
  29. });

简介

Promise 出现的原因

在 Promise 出现以前,我们处理一个异步网络请求,大概是这样:
回调地狱

  1. 请求1(function(请求结果1){
  2. 请求2(function(请求结果2){
  3. 请求3(function(请求结果3){
  4. 请求4(function(请求结果4){
  5. ...
  6. })
  7. })
  8. })
  9. })

回调地狱带来的负面作用有以下几点:

  • 代码臃肿。
  • 可读性差。
  • 耦合度过高,可维护性差。
  • 代码复用性差。
  • 容易滋生 bug。
  • 只能在回调里处理异常。

    什么是 Promise

    Promise 是异步编程的一种解决方案,比传统的异步解决方案【回调函数】和【事件】更合理、更强大。
    Promise支持链式调用, 解决回调地狱
    1. new Promise(请求1)
    2. .then(请求2(请求结果1))
    3. .then(请求3(请求结果2))
    4. .then(请求4(请求结果3))
    5. .catch(处理异常(异常信息))

    Promise 的状态

实例对象中的一个属性 『PromiseState』

  • pending 未决定的
  • resolved / fullfilled 成功
  • rejected 失败

Promise 对象的值

实例对象中的另一个属性 『PromiseResult』
保存着异步任务『成功/失败』的结果

  • resolve
  • reject

    几个关键问题

    改变Promise对象状态

    状态只能从pending => fulfilled (resolved)或者pending => rejected
    1. let p = new Promise((resolve, reject) => {
    2. //1. resolve 函数
    3. // resolve('ok'); // pending => fulfilled (resolved)
    4. //2. reject 函数
    5. // reject("error");// pending => rejected
    6. //3. 抛出错误
    7. // throw '出问题了';
    8. });

    Promise执行多个回调

    可以,每次都可以输出结果。 ```javascript let p = new Promise((resolve, reject) => { resolve(‘OK’); resolve(‘No’); //resolve多次调用无效 });

///指定回调 - 1 p.then(value => { console.log(value); //ok });

//指定回调 - 2 p.then(value => { alert(value); //ok });

  1. <a name="PV3dj"></a>
  2. ## 回调执行顺序
  3. 1. 如果没有异步任务,则先resolve,再执行then回调。
  4. 1. 如果有异步,则先微then后宏settimeout。then先指定回调函数,但不执行里边的回调函数,待resolve |reject执行后才调用
  5. ```javascript
  6. //这是异步情况
  7. let p = new Promise((resolve, reject) => {
  8. setTimeout(() => {
  9. resolve('OK');
  10. }, 1000);
  11. });
  12. p.then(value => {
  13. console.log(value);
  14. },reason=>{
  15. })

中断 Promise 链条

有且只有一个方式将状态改为pendding,因为抛出错误或reject会走catch

let p = new Promise((resolve, reject) => {
  setTimeout(() => {
    resolve('OK');
  }, 1000);
});

p.then(value => {
  console.log(111);
  // 有且只有一个方式将状态改为pendding
  // 因为抛出错误或reject会走catch
  return new Promise(() => {});
}).then(value => {
  console.log(222);
}).then(value => {
  console.log(333);
}).catch(reason => {
  console.warn(reason);
});

API

Promise (excutor) {}

Promise 构造函数: Promise (excutor) {}

  1. executor 函数: 执行器 (resolve, reject) => {}
  2. resolve 函数: 内部定义成功时我们调用的函数 value => {}
  3. reject 函数: 内部定义失败时我们调用的函数 reason => {} 说明: executor 会在 Promise 内部立即同步调用,异步操作在执行器中执行

Promise.prototype.then

实例方法,为 Promise 注册回调函数,函数形式:fn(vlaue){},value 是上一个任务的返回结果,then 中的函数一定要 return 一个结果或者一个新的 Promise 对象,才可以让之后的then 回调接收。

为什么promise可以链式调用?设置then方法返回的结果也是promise对象
返回非primise会怎样?如下
返回非primise会对获取回调(.then)产生什么影响? 没影响 依然是一个promise对象

//创建 promise 对象
const p = new Promise((resolve, reject)=>{
  setTimeout(()=>{
    resolve('用户数据');
    // reject('出错啦');
  }, 1000)
});

//调用 then 方法  then方法的返回结果是 Promise 对象, 对象状态由回调函数的执行结果决定
//1. 如果回调函数中返回的结果是 非 promise 类型的属性, 状态为成功, 返回值为对象的成功的值

const result = p.then(value => {
  console.log(value);
  //1. 非 promise 类型的属性
  // return 'iloveyou';
  //控制台输出的结果 状态为成功, 返回值为对象的成功的值
  //[[PromiseState]]: "fulfilled"
    //[[PromiseResult]]: "iloveyou"

  //2. 是 promise 对象
  // return new Promise((resolve, reject)=>{
  //     // resolve('ok');
  //     reject('error');
  // });
  //控制台输出的结果 状态为成功
  //[[PromiseState]]: "fulfilled"
    //[[PromiseResult]]: "ok"

  //3. 抛出错误
  // throw new Error('出错啦!');
  throw '出错啦!';
  //控制台输出的结果 状态为失败
  //[[PromiseState]]: "rejected"
    //[[PromiseResult]]: "出错啦!"

}, reason=>{
  console.warn(reason);
});

console.log(result)


Promise.resolve

类方法,.then的第一个参数, 异步操作成功后调用, 返回的 promise 对象的状态为 resolved 。

let p1 = Promise.resolve(521);
//如果传入的参数为 非Promise类型的对象, 则返回的结果为成功promise对象
//如果传入的参数为 Promise 对象, 则参数的结果决定了 resolve 的结果
let p2 = Promise.resolve(new Promise((resolve, reject) => {
  // resolve('OK');
  reject('Error');
}));
// console.log(p2);
p2.catch(reason => {
  console.log(reason);
})

Promise.reject

类方法,.then的第二个参数, 异步操作失败后调用, 且与 resolve 唯一的不同是,返回的 promise 对象的状态为 rejected。(结果永远是失败的 , 内容由传入值决定 即时传入一个成功的promise)

// let p2 = Promise.reject('iloveyou');
let p3 = Promise.reject(new Promise((resolve, reject) => {
  resolve('OK');
}));

console.log(p3);

Promise.prototype.catch

实例方法,捕获异常,函数形式:fn(err){}, err 是 catch 注册 之前的回调抛出的异常信息。

Promise()
    .then(function(data){
      console.log(data)
    })
    .then(function(data){
      console.log(data)
    })
  .cath(function(reason){ //捕获异常
        console.log(reason)
    })

Promise.all

类方法,多个 Promise 任务同时执行。 如果全部成功执行,则以数组的方式返回所有 Promise 任务的执行结果。 如果有一个 Promise 任务 rejected,则只返回 rejected 任务的结果。 必须所有操作都成功,否则报错

Promise
  .all([run1(),run2(),run3()])
  .then(function(data){
      console.log(data)
  })

Promise.allSettled

无论成功与否都会返回对应数据

//声明两个promise对象
const p1 = new Promise((resolve, reject)=>{
    setTimeout(()=>{
        resolve('商品数据 - 1');
    },1000)
});

const p2 = new Promise((resolve, reject)=>{
    setTimeout(()=>{
        // resolve('商品数据 - 2');
            reject('出错啦!');
    },1000)
});

//调用 allsettled 方法
const result = Promise.allSettled([p1, p2]);
console.log(result);

image.png

Promise.race

类方法,多个 Promise 任务同时执行,返回最先执行结束(谁跑得快以谁的 Promise 任务的结果为准) ,不管这个 Promise 结果是成功还是失败。

Promise
  .race([run1(),run2(),run3()])
  .then(function(data){
        console.log("我执行得最慢",data)
    })

resolve深度解析

两种方式创建, 这两种方式都会返回一个 Promise 对象。

  • 1、new Promise(fn)
  • 2、Promise.resolve(fn)

Promise 的常用 API 如下:

  • Promise.resolve(value) 看例子解析

    类方法,该方法返回一个以 value 值解析后的 Promise 对象 1、如果这个值是个 thenable(即带有 then 方法),返回的 Promise 对象会“跟随”这个 thenable 的对象,采用它的最终状态(指 resolved/rejected/pending/settled) 2、如果传入的 value 本身就是 Promise 对象,则该对象作为 Promise.resolve 方法的返回值返回。 3、其他情况以该值为成功状态返回一个 Promise 对象。

上面是 resolve 方法的解释,传入不同类型的 value 值,返回结果也有区别。这个 API 比较重要,建议大家通过练习一些小例子,并且配合上面的解释来熟悉它。如下几个小例子:

//如果传入的 value 本身就是 Promise 对象,则该对象作为 Promise.resolve 方法的返回值返回。  
function fn(resolve){
    setTimeout(function(){
        resolve(123);
    },3000);
}
let p0 = new Promise(fn);
let p1 = Promise.resolve(p0);
// 返回为true,返回的 Promise 即是 入参的 Promise 对象。
console.log(p0 === p1);

//解析
//第7行相当于以下代码。此时p0就是promise对象
//第8 如果传入的 value 本身就是 Promise 对象,则该对象作为 Promise.resolve 方法的返回值返回。
let p0 = new Promise(function(){
  setTimeout(function(){
        resolve(123);
    },3000);
});

传入 thenable 对象,返回 Promise 对象跟随 thenable 对象的最终状态。

ES6 Promises 里提到了 Thenable 这个概念,简单来说它就是一个非常类似 Promise 的东西。最简单的例子就是 jQuery.ajax,它的返回值就是 thenable 对象。但是要谨记,并不是只要实现了 then 方法就一定能作为 Promise 对象来使用。

//如果传入的 value 本身就是 thenable 对象,返回的 promise 对象会跟随 thenable 对象的状态。
let promise = Promise.resolve($.ajax('/test/test.json'));// => promise对象
promise.then(function(value){
   console.log(value);
});

返回一个状态已变成 resolved 的 Promise 对象。

let p1 = Promise.resolve(123); 
//打印p1 可以看到p1是一个状态置为resolved的Promise对象
console.log(p1)