es6之Promise是什么「建议收藏」

es6之Promise是什么「建议收藏」Promise的含义Promise是一个容器,内部保存着某个未来才会结束的事件(通常是一个异步操作)的结果。Promise也是一个对象,可以通过这个对象获取异步操作的消息。Promise的特点:对象的状态不受外部影响,只有异步操作的结果才能决定状态。一共有三种状态:pending(进行中)、fulfilled(成功的)和rejected(失败的)。对象的状态发生改变后,不会再变化,并且随时可以得到这个结果。对象的状态改变只有两种情况:pending=》fulfilled,pending=》reje

大家好,又见面了,我是你们的朋友全栈君。

Promise的含义

Promise是一个容器,内部保存着某个未来才会结束的事件(通常是一个异步操作)的结果。Promise也是一个对象,可以通过这个对象获取异步操作的消息。
Promise的特点:

  1. 对象的状态不受外部影响,只有异步操作的结果才能决定状态。一共有三种状态:pending(进行中)、fulfilled(成功的)和rejected(失败的)。
  2. 对象的状态发生改变后,不会再变化,并且随时可以得到这个结果。对象的状态改变只有两种情况:pending=》fulfilledpending=》rejected

Promise的缺点:

  1. 无法取消Promise,一旦创建就会立即执行
  2. 如果不设置回调函数,Promise内部抛出的错误,不会反应到外部。
  3. 当对象状态是pending时,无法得知当前进行到哪一步(刚刚开始还是即将完成)。

基本用法

  1. 创建Promise实例
const promise = new Promise(function(resolve, reject){ 
   
	// ...some code
	if (/*异步操作成功*/) { 
   
		resolve(value);
	} else { 
   
		reject(error);
	}
});

Promise构造函数接收一个函数作为参数,该函数的两个参数分别是resolve和reject。它们是两个函数,由JavaScript引擎提供,不用自己部署。
resolve函数的作用:将Promise对象状态从“未完成”变为“成功”(pending=》resolved)。在异步操作成功时调用,并将异步操作的结果作为参数传递出去。
reject函数的作用:将Promise对象状态从“未完成”变为“失败”(pending=》rejected)。在异步操作失败时调用,将异步操作爆出的错误,作为参数传递出去。

  1. 调用Promise
    Promise实例生成后,可以用then方法分别指定resolved状态和rejected状态的回调函数。
promise.then(function(value){ 
   
	//success Promise对象的状态变为resolved时调用
}, function(error){ 
   
	//failure Promise对象的状态变为rejected时调用,该函数为可选参数
});
  1. reject函数和resolve函数参数
    reject函数的参数通常是Error对象的实例。
    resolve函数的参数除了正常的值意外,还可能是另一个Promise实例。

Promise.prototype.then()

Promise实例具有then方法,即then方法是定义在原型对象Promise.prototype上的。它的作用是为Promise实例添加状态改变时的回调函数。then方法的第一个参数是resolved状态的回调函数,第二个参数(可选)是rejected状态的回调函数。
then方法返回的是一个新的Promise实例(不是原来的那个Promise实例)。因此可以采用链式写法,即then方法后面再调用另一个then方法。

// 第一个回调函数完成以后,会将返回结果作为参数,传入第二个回调函数,回调函数可以采用箭头函数形式,更加简洁
getJSON("/posts.json").then(function(json){ 
   
	return json.post;
}).then(function(post){ 
   
//...
});

Promise.prototype.catch()

Promise.prototype.catch()方法是.then(null, rejection).then(undefined, rejection)的别名,用于指定发生错误时的回调函数。

getJSON("/posts.json").then(fuction(posts){ 
   
	//...
}).catch(function(error){ 
   
	// 处理getJSON 和 前一个回调函数运行时发生的错误
});

如果没有使用catch()方法指定错误处理的回调函数,Promise对象抛出的错误不会传递到外层代码,即不会有任何反应。

Promise.prototype.finally()

finally()方法用于指定不管Promise对象最后状态如何,都会执行的操作。该方法是ES2018引入标准的。
finally方法的回调函数不接受任何参数,这意味着它不知道前面的Promise实例的状态,这表明,finally方法里面的操作,应该是和状态无关的,不依赖于Promise的执行结果。
finally本质上是then方法的特例。

Promise.all()

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

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

参数Promise.all()方法接收一个数组作为参数,p1、p2、p3都是Promise实例,如果不是,则调用Promise.resolve方法,将参数转化为Promise实例。
Promise.all()方法的参数可以不是数组,但必须具有Iterator接口。
新实例p的状态由p1、p2、p3决定,有两种情况

  1. fulfilled只有p1、p2、p3的状态都变成fulfilledp的状态才会变成fulfilled,此时,它们的返回值组成一个数组,传递给p的回调函数
  2. rejected只要p1、p2、p3之中有一个被rejectedp的状态就变成了rejected,此时,第一个被reject的实例的返回值,会传递给p的回调函数

注意:如果作为参数的Promise实例,自己定义了catch方法,那么它一旦被rejected,并不会触发Promise.all()catch方法。

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: 报错了]

原因p1resolvedp2rejected,但是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.all()方法一样,如果不是Promise实例,就会调用Promise.resolve()方法转化。

const p = Promise.race([
  fetch('/resource-that-may-take-a-while'),
  new Promise(function (resolve, reject) { 
   
    setTimeout(() => reject(new Error('request timeout')), 5000)
  })
]);

p
.then(console.log)
.catch(console.error);

上面代码中,如果5秒内fetch方法无法返回结果,结果p的状态就会变为rejected,从而触发catch方法指定的回调函数。

Promise.allSettled()

Promise.allSettled()方法接收一组Promise实例作为参数,包装成一个新的Promise实例。只有等到所有这些参数实例都返回结果,无论是fulfilled还是rejected,包装实例才会结束。该方法由ES2020引入。
新实例:返回新的Promise实例,一旦结束,状态总是fulfilled,不会变成rejected。状态变成fulfilled后,Promise的监听函数接收到的参数是一个数组,每个成员对应传入一个Promise.allSettled()的Promise实例。

const resolved = Promise.resolve(42);
const rejected = Promise.reject(-1);

const allSettledPromise = Promise.allSettled([resolved, rejected]);

allSettledPromise.then(function (results) { 
   
  console.log(results);
});
// [
// { status: 'fulfilled', value: 42 },
// { status: 'rejected', reason: -1 }
// ]

使用Promise.allSettled()方法可以确定所有传入的Promise实例的异步操作是否结束了。

Promise.any()

Promise.any()方法接收一组Promise实例作为参数,包装成一个新的Promise实例。
新实例新实例的状态由所有参数实例决定,有两种情况

  1. fulfilled只要参数实例有一个变成fulfilled状态,包装实例就会变成fulfilled
  2. rejected如果所有参数实例都变成rejected状态,包装状态就会变成rejected状态
var resolved = Promise.resolve(42);
var rejected = Promise.reject(-1);
var alsoRejected = Promise.reject(Infinity);

Promise.any([resolved, rejected, alsoRejected]).then(function (result) { 
   
  console.log(result); // 42
});

Promise.any([rejected, alsoRejected]).catch(function (results) { 
   
  console.log(results); // [-1, Infinity]
});

Promise.resolve()

Promise.resolve()方法可以把现有对象转为Promise对象。

Promise.resolve('foo')
// 等价于
new Promise(resolve => resolve('foo'))
  1. 参数是一个 Promise 实例
    不做任何修改。
  2. 参数是一个thenable对象
    thenable对象是指具有then方法的对象。如下:
let thenable = { 
   
  then: function(resolve, reject) { 
   
    resolve(42);
  }
};

转化为Promise对象,然后执行thenable对象的then方法。
3. 参数不是具有then方法的对象,或根本就不是对象

const p = Promise.resolve('Hello');

p.then(function (s){ 
   
  console.log(s)
});
// Hello

返回的Promise实例一生成就是resolved,所以回调函数会立即执行。
4. 不带有任何参数
Promise.resolve()方法允许调用时不带参数,直接返回一个resolved状态的Promise对象(没有参数)。

const p = Promise.resolve();

p.then(function () { 
   
  // ...
});

Promise.reject()

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

const p = Promise.reject('出错了');
// 等同于
const p = new Promise((resolve, reject) => reject('出错了'))

p.then(null, function (s) { 
   
  console.log(s)
});
// 出错了

注意Promise.reject()方法的参数,会原封不动地作为reject的理由,变成后续方法的参数。这一点与Promise.resolve方法不一致。
如果传入一个thenable对象,那回调函数的参数就是这个对象。

应用

  1. 加载图片
    加载完,根据成功与否(Promise状态变化),去回调函数,进行后续操作。
const preloadImage = function (path) { 
   
  return new Promise(function (resolve, reject) { 
   
    const image = new Image();
    image.onload  = resolve;
    image.onerror = reject;
    image.src = path;
  });
};
  1. Generator函数与Promise的结合

Promise.try()

需求:不区分函数f是同步函数还是异步操作,但是想用Promise来处理它。因为这样就可以不管f是否包含异步操作,都用then方法指定下一步流程,用catch方法处理f抛出的错误。
一般写法

const f = () => console.log('now');
Promise.resolve().then(f);
console.log('next');
// next
// now

一般写法缺点:如果f是同步函数,它会在本轮事件循环的末尾执行。
改进写法1

const f = () => console.log('now');
(async () => f())(); // 立即执行的匿名函数
console.log('next');
// now
// next

第二行的匿名函数立即执行里面的async函数,如果f同步就会得到同步结果,如果f异步,就可以用then指定下一步,catch捕获错误。如果不使用catch方法捕获错误,async() => f()会吃掉f()抛出的错误。
改进写法2

const f = () => console.log('now');
(
  () => new Promise(
    resolve => resolve(f())
  )
)();
console.log('next');
// now
// next

同样是立即执行的匿名函数,创建一个新的Promise实例,调用resolve方法,把f函数作为参数传入。这种情况下,同步函数也是同步执行的。
改进方法3

const f = () => console.log('now');
Promise.try(f);
console.log('next');
// now
// next

Promise.try()方法替代上面两种写法,为所有操作提供了统一的处理机制,还可以更好地管理异常。
管理异常
一个Promise对象抛出异步错误,可以用catch方法捕获,如下:

database.users.get({ 
   id: userId})
.then(...)
.catch(...)

但如果该对象抛出了同步错误,就需要使用try...catch去捕获。

try { 
   
  database.users.get({ 
   id: userId})
  .then(...)
  .catch(...)
} catch (e) { 
   
  // ...
}

使用Promise.try()方法生成的实例,可以统一调用promise.catch()捕获所有同步和异步的错误。

Promise.try(() => database.users.get({ 
   id: userId}))
  .then(...)
  .catch(...)

Promise.try模拟try代码块,promise.catch模拟catch代码块。

学习资料

ECMAScript 6 入门

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请联系我们举报,一经查实,本站将立刻删除。

发布者:全栈程序员-站长,转载请注明出处:https://javaforall.net/139844.html原文链接:https://javaforall.net

(0)
全栈程序员-站长的头像全栈程序员-站长


相关推荐

  • Xshell 连接linux主机

    Xshell 连接linux主机0 前言使用 Xshell 连接远程服务器 文件 新建 出现如下图标 主机即为需要连接的 Linux 服务器的 ip 地址 端口号为 22 无须修改 但需要确认远程服务器的 22 端口已经打开 点击左边的 用户身份验证 输入用户名和密码 点击确认后 即可连接 总体流程 就是这个样子 问题就在于 ip 地址 用户名 密码怎么填写 下面几个章节就展示了如何查看远程 Linux 服务器的 ip 用户名和密码等 1 查看 ip 地址查看 ip 地址使用命令 ifconfig 确保能够 ping 通在连接之前 需要确保本地能够 p

    2025年7月11日
    4
  • 数据结构教程 视频_神谷哲史男巫教程视频

    数据结构教程 视频_神谷哲史男巫教程视频史上最全的数据结构视频教程打包下载地址本文出自出自我是码农,转载请注明出处,谢谢!以下数据结构视频教程是我多年收集的,因为在百度网盘上分享整个教程很快就会被delete,所以我只好花费大量功夫对单个视频进行一个一个的分享,这样才能长时间保留下来,为了学习,麻烦些也值得了!现在毫无保留的免费共享给大家,与君共勉!个人认为数据结构这门课程比较难,看视频学习是最好的方式。当年我就是看了多个视频,然…

    2022年10月12日
    2
  • 模拟 表单 取值问题

    模拟 表单 取值问题

    2021年5月26日
    95
  • scrapy环境配置_python的安装环境

    scrapy环境配置_python的安装环境本文仅供学习交流使用,如侵立删!联系方式及demo下载见文末PythonScrapy安装及相关配置环境win10Python:3.6.7Scrapy:2.4.1Python安装Scrapy安装pipinstallscrapy解决Command“pythonsetup.pyegg_info”failedwitherrorcode1问题更新pippipinstall–upgradepip问题:权限不足解决:pip

    2026年1月20日
    3
  • java输入数组元素_java数组的输出

    java输入数组元素_java数组的输出1.简介Java中快捷输出数组中各个元素笔者目前所知的就三种方法,今天就简单的做个记录。大家如果有什么更好的方法,麻烦留言评论。2.代码publicclassArrayPrint{publicstaticvoidmain(String[]args){int[]arrays1=newint[]{1,2,3,4};//ThefirstmethodSystem.out…

    2022年8月31日
    4
  • 增量表全量表拉链表区别_hive 增量数据更新

    增量表全量表拉链表区别_hive 增量数据更新一、概念增量表:记录更新周期内新增的数据,即在原表中数据的基础上新增本周期内产生的新数据;全量表:记录更新周期内的全量数据,无论数据是否有变化都需要记录;拉链表:一种数据存储和处理的技术方式,可以记录数据的历史信息,记录数据从开始一直到当前所有变化的信息。二、举例详解增量表:以页面访问数据表为例,假设该表从2020-06-01开始记录数据,按天更新,分区为dt。2020-06-01产生了三条访问数据,如下表:2020-06-02首页和商详页又产生了2条访问数据,该两条即为2020-06-

    2022年10月17日
    3

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

关注全栈程序员社区公众号