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 入门

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

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

【正版授权,激活自己账号】: Jetbrains全家桶Ide使用,1年售后保障,每天仅需1毛

【官方授权 正版激活】: 官方授权 正版激活 支持Jetbrains家族下所有IDE 使用个人JB账号...

(0)


相关推荐

  • [数据库] 第一范式、第二范式、第三范式、BC范式

    [数据库] 第一范式、第二范式、第三范式、BC范式数据描述术语对应表关键码完全依赖、部分依赖、传递依赖第一范式、第二范式、第三范式

  • 用拉普拉斯变换求零状态响应_什么是UPS?为什么用UPS?关于UPS电源的知识都在这里!…[通俗易懂]

    用拉普拉斯变换求零状态响应_什么是UPS?为什么用UPS?关于UPS电源的知识都在这里!…[通俗易懂]来源:电气设计圈如有侵权,请联系删除UPS种类、功能、原理1什么是UPSUPS-UninterruptedPowerSystem;利用电池化学能作为后备能量,在市电断电等电网故障时,不间断地为用户设备提供(交流)电能的一种能量转换装置。2为什么用UPSUPS的四大功能:1不停电功能,解决电网停电问题;2交流稳压功能,解决网压剧烈波动问题;3净化功能,解决电网与电源污染问题;4管理功能…

  • 静态变量存储在哪个区

    静态变量存储在哪个区美团2017校园招聘Android静态变量存储在__区A全局区B堆C栈D常量区2017年3月21日19:00开始笔试,选项凭回忆打的,未全匹配。本人参考答案:A知识点内存到底分几个区?1、栈区(stack)—由编译器自动分配释放,存放函数的参数值,局部变量的值等。2、堆区(heap)—一般由程序员分配释放,若程序员不释放,程

  • 增强for循环的语法_增强for循环原理

    增强for循环的语法_增强for循环原理一、概述:也成为【foreach】循环,是JDK1.5版本以后出来的一个高级for循环,专门用来遍历数组和集合的,它的内部原理其实就是iterator迭代器,所以在遍历过程中,不能对集合的元素进行增删操作。二、格式for(元素的数据类型变量名:数组名/集合名{}三、代码演示publicstaticvoidmain(String[]args)…

    2022年10月28日
  • db4o发布7.2,出现.NET 3.5版本,支持LINQ「建议收藏」

    db4o发布7.2,出现.NET 3.5版本,支持LINQ「建议收藏」db4o发布7.2,出现.NET3.5版本,支持LINQDb4Object刚刚发布了db4o的7.2beta,除了以前支持如下的平台:.NET1.1,.NET2.0,Mono外,现在还支持.NET3.5了。当然支持.NET3.5,最主要的时候要来支持LINQ。关于LINQ,我稍后再讲。现在讲讲7.2中最大的新特性——TransparentActivation(透明…

  • jmeter安装及使用

    jmeter安装及使用1、下载好之后,解压到一个目录;2、配置环境变量:2.1、新建:JMETER_HOME,路径为上一步解压得到的路径:D:\software\apache-jmeter-2.92.2、CLASSPATH后面添加英文“;”,然后再添加%JMETER_HOME\lib\ext\ApacheJMeter_core.jar;%JMETER_HOME%\lib\jor…

发表回复

您的电子邮箱地址不会被公开。

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