资讯专栏INFORMATION COLUMN

Promise 对象的理解

church / 1558人阅读

摘要:使用对象的好处在于可以将异步操作以同步操作的流程表达出来,避免了层层嵌套的回调函数。对象异步操作抛出错误,状态就会变为,就会调用方法指定的回调函数处理这个错误。

Promise 含义

Promise 是异步编程的一种解决方案,比传统的解决方案——回调函数和事件——更合理和更强大。它由社区最早提出和实现,ES6 将其写进了语言标准,统一了用法,原生提供了 Promise 对象。

所谓 Promise,简单说就是一个容器,里面保存着某个未来才会结束的事件(通常是一个异步操作)的结果。从语法上说,Promise 是一个对象,从它可以获取异步操作的消息。Promise 提供统一的 API,各种异步操作都可以用同样的方法进行处理。

Promise 对象有以下两个特点:

对象的状态不受外界影响。有三种状态,分别为 pending(进行中)、fulfilled(已成功)和 rejected(已失败)。

一旦状态改变,就不会再变,任何时候都可以得到这个结果。状态改变只有两种可能:从 pending 变为 fulfilled 和从 pending 变为 rejected。

使用 Promise 对象的好处在于:

可以将异步操作以同步操作的流程表达出来,避免了层层嵌套的回调函数。

Promise 对象提供统一的接口,使得控制异步操作更加容易。

Promise 缺点:

无法取消 Promise,一旦新建它就会立即执行,无法中途取消。

如果不设置回调函数,Promise 内部抛出的错误,不会反应到外部。

当处于 pending 状态时,无法得知目前进展到哪一个阶段(刚刚开始还是即将完成)。

基本用法

ES6 规定,Promise 对象是一个构造函数,用来生成 Promise 实例。

const promise = new Promise((resolve, reject) => {
  setTimeout(() => {
    const num = Math.random();

    if (num > 0.5) {
      resolve(num);
    } else {
      reject(num);
    }
  }, 500);
});

promise.then(
  res => {
    console.log("成功:" + res);
  },
  err => {
    console.log("失败:" + err);
  }
);

Promise 构造函数接受一个函数作为参数,该函数的两个参数分别是 resolve 和 reject。它们是两个函数,由 JavaScript 引擎提供,不用自己部署。

resolve 函数的作用:将 Promise 对象的状态从“未完成(pending)”变为“成功(resolved)”,在异步操作成功时调用,并将异步操作的结果作为参数传递出去。

reject 函数的作用:将 Promise 对象的状态从“未完成(pending)”变为“失败(rejected)”在异步操作失败时调用,并将异步操作报出的错误,作为参数传递出去。

then 方法作用:接受两个回调函数作为参数。第一个回调函数是 Promise 对象的状态变为 resolved 时调用,第二个回调函数是 Promise 对象的状态变为 rejected 时调用。第二个函数可选,不一定要提供,也可以将第二个函数作为 catch 方法的参数。

catch 方法作用:用于指定发生错误时的回调函数。Promise 对象异步操作抛出错误,状态就会变为 rejected,就会调用 catch 方法指定的回调函数处理这个错误。另外,then 方法指定的回调函数,如果运行中抛出错误,也会被 catch 方法捕获。

const promise = new Promise((resolve, reject) => {
  setTimeout(() => {
    const num = Math.random();

    if (num > 0.5) {
      resolve(num);
    } else {
      reject(num);
    }
  }, 500);
});

promise
  .then(res => {
    console.log("成功:" + res);
  })
  .catch(err => {
    console.log("失败:" + err);
  });

promise
  .then(res => {
    console.log("成功:" + res);
    throw new Error("test");
  })
  .catch(err => {
    // num > 0.5时打印 "失败:Error: test"
    console.log("失败:" + err);
  });
Promise 执行顺序

Promise 新建后立即执行,then 方法指定的回调函数,将在当前脚本所有同步任务执行完才会执行,catch 同理。

调用 resolve 或 reject 并不会终结 Promise 的参数函数的执行。

const promise = new Promise((resolve, reject) => {
  console.log("我是第一个执行的");
  resolve();
});

promise.then(res => {
  console.log("我是第三个执行的");
});

console.log("我是第二个执行的");
resolve 函数和 reject 函数的参数

reject 函数的参数通常是 Error 对象的实例,表示抛出的错误;resolve 函数的参数除了正常的值以外,还可能是另一个 Promise 实例。

如果一个 Promise(P2) 的 resolve 参数是另一个 Promise(P1),此时 P1 的状态就会传给 P2,P1 的状态决定了 P2 的状态,P1 的状态改变,P2 的回调函数才会执行。

const p1 = new Promise(function(resolve, reject) {
  setTimeout(() => reject(new Error("fail")), 3000);
});

const p2 = new Promise(function(resolve, reject) {
  setTimeout(() => resolve(p1), 1000);
});

p2.then(result => console.log(result)).catch(error => console.log(error));
// Error: fail

上面代码中,p1 是一个 Promise,3 秒之后变为 rejected。p2 的状态在 1 秒之后改变,resolve 方法返回的是 p1。由于 p2 返回的是另一个 Promise,导致 p2 自己的状态无效了,由 p1 的状态决定 p2 的状态。所以,后面的 then 语句都变成针对后者(p1)。又过了 2 秒,p1 变为 rejected,导致触发 catch 方法指定的回调函数。

Promise 链式调用

then 方法可以返回一个新的 Promise 实例(注意,不是原来那个 Promise 实例)。因此可以采用链式写法,即 then 方法后面再调用另一个 then 方法。

const promise = new Promise((resolve, reject) => {
  resolve("promise");
})
  .then(res => {
    console.log(res); // promise
    return "promise1";
  })
  .then(res => {
    console.log(res); // promise1
    return "promise2";
  })
  .then(res => {
    console.log(res); // promise2
  });

注意:只要一个 Promise 中抛出错误,将执行 catch 方法,then 链终止。

const promise = new Promise((resolve, reject) => {
  resolve("promise");
})
  .then(res => {
    console.log(res); // promise
    throw new Error("中止");
    return "promise1";
  })
  .then(res => {
    console.log(res);
    return "promise2";
  })
  .then(res => {
    console.log(res);
  })
  .catch(err => {
    console.log(err); // Error: 中止
  });

主动终止 then 链,通过 catch 方法来中止 promise chain

const promise = new Promise((resolve, reject) => {
  resolve("promise");
})
  .then(res => {
    console.log(res); // promise
    return Promise.reject({
      notRealPromiseException: true
    });
  })
  .then(res => {
    console.log(res);
    return "promise2";
  })
  .then(res => {
    console.log(res);
  })
  .catch(err => {
    if (err.notRealPromiseException) {
      return true;
    }
    console.log(err);
  });
Promise.prototype.finally()

finally 方法用于指定不管 Promise 对象最后状态如何,都会执行的操作。该方法是 ES2018 引入标准的。

finally 本质上是 then 方法的特例,不接受任何参数,不依赖于 Promise 的执行结果

promise.finally(() => {
  // 语句
});

// 等同于
promise.then(
  result => {
    // 语句
    return result;
  },
  error => {
    // 语句
    throw error;
  }
);
Promise.all()

Promise.all 方法用于将多个 Promise 实例,包装成一个新的 Promise 实例。

const promise = Promise.all([promise1, promise2, promise3])

Promise.all 方法接受一个数组作为参数,promise、pro 米色、promise3 都是 Promise 实例,如果不是,就会先调用下面讲到的 Promise.resolve 方法,将参数转为 Promise 实例,再进一步处理。(Promise.all 方法的参数可以不是数组,但必须具有 Iterator 接口,且返回的每个成员都是 Promise 实例。了解 Iterator 接口)

promise 的状态由 promise1、promise2、promise3 决定,分成两种情况。

只有 promise1、promise2、promise3 的状态都变成 fulfilled,p 的状态才会变成 fulfilled,此时 promise1、promise2、promise3 的返回值组成一个数组,传递给 p 的回调函数。

只要 promise1、promise2、promise3 之中有一个被 rejected,promise 的状态就变成 rejected,此时第一个被 reject 的实例的返回值,会传递给 promise 的回调函数。

const p1 = new Promise((resolve, reject) => {
  resolve("hello");
})
  .then(result => result)
  .catch(e => e);

const p2 = new Promise((resolve, reject) => {
  throw new Error("报错了");
})
  .then(result => result)
  .catch(e => e);

Promise.all([p1, p2])
  .then(result => console.log(result))
  .catch(e => console.log(e));
// ["hello", Error: 报错了]

上面代码中,p1 会 resolved,p2 首先会 rejected,但是 p2 有自己的 catch 方法,该方法返回的是一个新的 Promise 实例,p2 指向的实际上是这个实例。该实例执行完 catch 方法后,也会变成 resolved,导致 Promise.all()方法参数里面的两个实例都会 resolved,因此会调用 then 方法指定的回调函数,而不会调用 catch 方法指定的回调函数。

如果 p2 没有自己的 catch 方法,就会调用 Promise.all()的 catch 方法。

Promise.race()

Promise.race 方法同样是将多个 Promise 实例,包装成一个新的 Promise 实例。

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

只要 p1、p2、p3 之中有一个实例率先改变状态,p 的状态就跟着改变。那个率先改变的 Promise 实例的返回值,就传递给 p 的回调函数。

Promise.race 方法的参数与 Promise.all 方法一样,如果不是 Promise 实例,就会先调用下面讲到的 Promise.resolve 方法,将参数转为 Promise 实例,再进一步处理。

Promise.resolve()

将现有对象转化为 Promise 对象。

const promise = Promise.resolve("Hello world")

参数是 Promise 实例,该方法不做任何改变。

参数是一个 thenable 对象,先将对象转为 Promise 对象,然后立即执行 thenable 方法。相当于将 thenable 对象中的 then 方法处理的值作为参数传给 promise then 方法。

let thenObj = {
  then(resolve, reject) {
    resolve("Hello");
  }
};

const promise = Promise.resolve(thenObj);
promise.then(res => {
  console.log(res); // Hello
});

参数不是具有 then 方法的对象,或根本就不是对象,则 Promise.resolve 方法返回一个新的 Promise 对象,状态为 resolved。

Promise.reject()

Promise.reject(reason)方法也会返回一个新的 Promise 实例,该实例的状态为 rejected。

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/103253.html

相关文章

  • Promise理解

    摘要:中就是一个构造函数函数也是对象为什么需要多个嵌套的异步操作,如果直接用方式,会导致的出现使得异步操作更加规范,更加统一。的方法构造函数构造函数用于生成对象,函数在构造函数执行时同步执行。 什么是Promise? 含以上:抽象异步操作的工具。javascript中:Promise就是一个构造函数(函数也是对象) 为什么需要Promise? 1.多个嵌套的异步操作,如果直接用callbac...

    Binguner 评论0 收藏0
  • JavaScript 异步

    摘要:从最开始的到封装后的都在试图解决异步编程过程中的问题。为了让编程更美好,我们就需要引入来降低异步编程的复杂性。写一个符合规范并可配合使用的写一个符合规范并可配合使用的理解的工作原理采用回调函数来处理异步编程。 JavaScript怎么使用循环代替(异步)递归 问题描述 在开发过程中,遇到一个需求:在系统初始化时通过http获取一个第三方服务器端的列表,第三方服务器提供了一个接口,可通过...

    tuniutech 评论0 收藏0
  • promise async await 理解笔记

    摘要:在异步编程中,提供了对象的方式。例如等同于所以可以理解为生成一个实例。那么自然也可以去调用则是配合使用的。等于是等待一个返回值,等待的执行结果。但是函数不会造成阻塞,所以配合使用,则没有影响到外部。 在异步编程中,es6提供了promise对象的方式。简单的用法 var promise = new Promise((resolve,reject)=>{ if(){ ...

    NoraXie 评论0 收藏0
  • 理解 Javascript 中 Promise

    摘要:理解承诺有两个部分。如果异步操作成功,则通过的创建者调用函数返回预期结果,同样,如果出现意外错误,则通过调用函数传递错误具体信息。这将与理解对象密切相关。这个函数将创建一个,该将在到秒之间的随机数秒后执行或。 想阅读更多优质文章请猛戳GitHub博客,一年百来篇优质文章等着你! showImg(https://segmentfault.com/img/bVbkNvF?w=1280&h=...

    paulli3 评论0 收藏0
  • 理解 Javascript 中 Promise

    摘要:理解承诺有两个部分。如果异步操作成功,则通过的创建者调用函数返回预期结果,同样,如果出现意外错误,则通过调用函数传递错误具体信息。这将与理解对象密切相关。这个函数将创建一个,该将在到秒之间的随机数秒后执行或。 想阅读更多优质文章请猛戳GitHub博客,一年百来篇优质文章等着你! showImg(https://segmentfault.com/img/bVbkNvF?w=1280&h=...

    chaos_G 评论0 收藏0
  • [译] 深入理解 Promise 五部曲:4. 扩展问题

    摘要:有一个和相关的更大的问题。最后,请负有责任感并且使用安全的扩展。深入理解五部曲异步问题深入理解五部曲转换问题深入理解五部曲可靠性问题深入理解五部曲扩展性问题深入理解五部曲乐高问题最后,安利下我的个人博客,欢迎访问 原文地址:http://blog.getify.com/promis... 现在,我希望你已经看过深入理解Promise的前三篇文章了。并且假设你已经完全理解Promises...

    Shimmer 评论0 收藏0

发表评论

0条评论

church

|高级讲师

TA的文章

阅读更多
最新活动
阅读需要支付1元查看
<