侧边栏壁纸
博主头像
uvdream博主等级

一切皆有可能!

  • 累计撰写 37 篇文章
  • 累计创建 21 个标签
  • 累计收到 18 条评论
es6

【查漏补缺】Promise

uvdream
2021-09-04 / 0 评论 / 13 点赞 / 468 阅读 / 7,059 字
温馨提示:
本文最后更新于 2022-04-08,若内容或图片失效,请留言反馈。部分素材来自网络,若不小心影响到您的利益,请联系我们删除。

介绍

Promise/A+规范

  • 存在三个状态:等待(pending),执行中(fulfilled),失败(rejected)
  • 初始态为等待态,可转换为执行中和失败
  • 执行中不可转换为其它状态,且必须有一个不可变的终值(value)
  • 失败状态不可转换为其它状态,且必须有一个不可变的原因(reason)
  • 必须提供一个then方法,以供访问其当前值,终值以及原因
  • then方法提供两个参数:onFulfilledonRejected
  • onFulfilledonRejected如果不是函数类型,必须忽略
  • 如果executor执行报错,直接执行reject
  • 不同的promise可以相互套用

更多参考 promise/A+

Promise 起源与用途

  • Promise最早在社区提出和实现,在es6写入了语言标准
  • Promise是异步编程的解决方案,比传统的回调函数方式更加合理更加强大更加有呀
  • 语法上:使用Promise构造函数对异步操作进行封装以生成Promise实例
  • 功能上:Promise对象用来封装一个异步操作并提供统一的 API,使用各种异步操作都可以用同步的方式进行处理

常用场景

  • fs文件操作
require("fs").readFile("./index.html", (err, data) => {});
  • Ajax操作
$.get("/api/getUser", (data) => {});
  • 定时器
setTimeout(() => {}, 1000);

为什么使用Promise

  • 支持链式调用,将异步操作以同步操作的流程表达出来
  • 可以解决回调地狱
// 回调地狱典型场景

asyncFunc1(opt, (...args1) => {

  asyncFunc2(opt, (...args2) => {

    asyncFunc3(opt, (...args3) => {

      asyncFunc4(opt, (...args4) => {

        //TODO: some opt

      });

    });

  });

});
  • 指定回调函数的方式更加灵活

    • 传统方式:必须在启动异步任务之前指定
    • Promise:可以随时监听任务的状态,随时指定回调函数,一个或者多个
  • Promise提供了统一的 api,使得控制异步操作更加容易

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内部跑出的错误,无法反映到外部
  • pedding状态时无法知道进展到哪一个阶段

Promise使用

实例创建

const promise = new Promise((resolve, reject) => {

    if (/*成功*/) {

        resolve(value)

    } else {

        reject(error)

    }

})
  • resolve函数:将Promise对象的状态从"未完成"变成"成功"(pedding=>fulfilled),在异步操作成功时调用,并将异步操作的结果,作为参数传递出去
  • reject函数:将Promise对象的状态从"未完成"变成"失败"(pedding=>reject),在异步操作失败时调用,并将异步操作报错的错误作为参数传递出去

例子(异步加载图片)

<body>

  <div id="app"></div>

</body>

<script>

  function loadImageAsync(url) {

    return new Promise(function (resolve, reject) {

      const image = new Image();


      image.onload = function () {

        resolve(image);

      };


      image.onerror = function () {

        reject(new Error("无法加载图片 " + url));

      };


      image.src = url;

    });

  }

  var img = loadImageAsync("http://www.pic.uvdream.cn/1616131986开发计划.png");

  img.then((res) => {

    console.log(res);

    var d = document.getElementById("app");

    d.appendChild(res);

  });

</script>

Promise.prototype.then()

promise.then(

  function (value) {

    // success

  },

  function (error) {

    // failure

  }

);

Promise.prototype.catch()

promise

  .then(

    function (value) {

      // success

    },

    function (error) {

      // failure

    }

  )

  .catch(function (error) {

    console.log("发生错误", error);

  });

如果 then()方法中出现错误也会被 catch()捕捉

const promise = new Promise(function (resolve, reject) {

  throw new Error("test");

});

promise.catch(function (error) {

  console.log(error);

});

也可以下面写法

// 写法一

const promise = new Promise(function (resolve, reject) {

  try {

    throw new Error("test");

  } catch (e) {

    reject(e);

  }

});

promise.catch(function (error) {

  console.log(error);

});


// 写法二

const promise = new Promise(function (resolve, reject) {

  reject(new Error("test"));

});

promise.catch(function (error) {

  console.log(error);

});

如果Promise状态已经变成resolved,再抛出错误是无效的

const promise = new Promise(function (resolve, reject) {

  resolve("ok");

  throw new Error("test");

});

promise

  .then(function (value) {

    console.log(value);

  })

  .catch(function (error) {

    console.log(error);

  });

以上代码是不会抛出错误的

Promise.resolve()

返回成功或者失败的Promise对象

Prmose.reject()

返回一个失败的Promise对象

Promise.all()

方法将多个Promise实例,包装成一个新的Promise实例

const p= Promise.all([p1,p2,p3])

p 的状态由 p1,p2,p3 决定

  • 只有 p1,p2,p3 的状态都编程fulfilled,p 的状态才会编程fulfilled,此时 p1,p2,p3 的返回值组成一个数组,传递给 p 的回调函数
  • 只要 p1,p2,p3 之中有一个rejected,p 的状态就变成rejected,此时第一个rejected的实例的返回值,会传递给 p 的回调函数

例子

let p1 = Promise.resolve(1);

let p2 = Promise.resolve(2);

let p3 = Promise.reject(3);

const res = Promise.all([p1, p2, p3]); //此时输出为 [[PromiseState]]是rejected,[[PromiseResult]]是3的Promise

const res = Promise.all([p1, p2]); //此时输出为 [[PromiseState]]是fulfilled,[[PromiseResult]]是[1,2]的Promise

Promise.prototype.finally()

finally()方法用于指定不管Promise对象最后状态如何,都会执行的操作,

finally()其实就是then()方法的特例

promise.finally(() => {

  //...

});

// 等同于

promise.then(

  (result) => {

    //...

    return result;

  },

  (error) => {

    //...

    throw error;

  }

);

Promise.race()

同样将多个Promise实例包装成一个新的Promise实例`

Promise.allSettled()

接受一组Promise实例作为参数,包装秤一个新的Promise实例,只有等到所有这些实例都返回结果,不管是fulfilled还是rejected,包装实例才会结束

Promise.any()

该方法接受一组 Promise 实例作为参数,包装成一个新的 Promise 实例返回。只要参数实例有一个变成fulfilled状态,包装实例就会变成fulfilled状态;如果所有参数实例都变成rejected状态,包装实例就会变成rejected状态。

手写 Promise

ES6

class Promise {

  static PENDING = "pending";

  static FULFILLED = "fulfilled";

  static REJECTED = "rejected";

  constructor(executor) {

    // 保存promise状态

    this.status = Promise.PENDING;

    // 保存promise结果

    this.value = null;

    // 解决setTimeOut问题,保存异步回调列表

    this.callbacks = [];

    try {

      executor(this.resolve.bind(this), this.rejected.bind(this));

    } catch (error) {

      this.rejected(error);

    }

  }

  resolve(value) {

    if (this.status === Promise.PENDING) {

      this.status = Promise.FULFILLED;

      this.value = value;

      setTimeout(() => {

        // 解决setTimeOut问题

        this.callbacks.map((callback) => {

          callback.onResolved(value);

        });

      });

    }

  }

  rejected(value) {

    if (this.status === Promise.PENDING) {

      this.status = Promise.REJECTED;

      this.value = value;

      setTimeout(() => {

        // 解决setTimeOut问题

        this.callbacks.map((callback) => {

          callback.onRejected(value);

        });

      });

    }

  }

  then(onResolved, onRejected) {

    if (typeof onResolved !== "function") {

      // then()的穿透解决

      onResolved = (value) => value;

    }

    if (typeof onRejected !== "function") {

      // then()的穿透解决

      onRejected = (value) => value;

    }

    let promise = new Promise((resolve, rejected) => {

      if (this.status === Promise.PENDING) {

        this.callbacks.push({

          onResolved: (value) => {

            this.parse(promise, onResolved(value), resolve, rejected);

            // try {

            //     let result = onResolved(value)

            //     if (result instanceof Promise) {

            //         result.then(resolve, rejected)

            //     } else {

            //         resolve(result)

            //     }

            // } catch (error) {

            //     // onRejected(error)

            //     rejected(error)

            // }

          },

          onRejected: (value) => {

            this.parse(promise, onRejected(value), resolve, rejected);

            // try {

            //     let result = onRejected(value)

            //     if (result instanceof Promise) {

            //         result.then(resolve, rejected)

            //     } else {

            //         resolve(result)

            //     }

            // } catch (error) {

            //     // onRejected(error)

            //     rejected(error)

            // }

          },

        });

      }

      if (this.status === Promise.FULFILLED) {

        setTimeout(() => {

          this.parse(promise, onResolved(this.value), resolve, rejected);

          // try {

          //     let result = onResolved(this.value)

          //     if (result instanceof Promise) {

          //         result.then(resolve, rejected)

          //     } else {

          //         resolve(result)

          //     }

          // } catch (error) {

          //     // onRejected(error)

          //     rejected(error)

          // }

        });

      }


      if (this.status === Promise.REJECTED) {

        setTimeout(() => {

          this.parse(promise, onRejected(this.value), resolve, rejected);

          // try {

          //     let result = onRejected(this.value)

          //     if (result instanceof Promise) {

          //         result.then(resolve, rejected)

          //     } else {

          //         resolve(result)

          //     }

          // } catch (error) {

          //     // onRejected(error)

          //     rejected(error)

          // }

        });

      }

    });

    return promise;

  }

  parse(promise, result, resolve, rejected) {

    if (promise === result) {

      throw new Error("不能返回promise");

    }

    try {

      // let result = onResolved(this.value)

      if (result instanceof Promise) {

        result.then(resolve, rejected);

      } else {

        resolve(result);

      }

    } catch (error) {

      // onRejected(error)

      rejected(error);

    }

  }

  static resolve(value) {

    return new Promise((resolve, reject) => {

      if (value instanceof Promise) {

        value.then(resolve, reject);

      } else {

        resolve(value);

      }

    });

  }


  static reject(value) {

    return new Promise((resolve, reject) => {

      reject(value);

    });

  }

  static all(promises) {

    const values = [];

    return new Promise((resolve, reject) => {

      promises.forEach((promise) => {

        promise.then(

          (value) => {

            values.push(value);

            if (values.length === promises.length) {

              resolve(value);

            }

          },

          (reason) => {

            reject(reason);

          }

        );

      });

    });

  }

}
0

评论区