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)


相关推荐

  • 富集分析集锦(KEGG富集分析图)

    链接:https://www.jianshu.com/p/988d90484f77不管是转录组,还是芯片数据,或者其他有关基因的组学分析,每当数据分析到后面,要想得到结果,都躲不过这个富集分析,因为它是帮助我们从庞杂的组学数据中发掘规律重要的一环,对基因功能进行富集分析,就有可能发现在生物学过程中起关键作用的生物通路,并且帮助理解生物学过程的分子机制。现在的高通量测序带来的巨大数据量,让我们眼…

  • 我的 Java 自学之路[通俗易懂]

    我的 Java 自学之路[通俗易懂]其实在转正之后我就想抽个时间好好的梳理一下我的Java上车之路,但是一直拖到现在,因为有学弟问到,所以也就给了我动力。毕竟答应了人家的事要做到。首先要有相应的背景介绍,不然说个毛线啊,大家不在同一水平,不好参考借鉴。我呢,学校很牛逼,是一所刚过线的二本,自身的成绩在班里也就第8名左右吧(一共60个人),在大二的时候学校开设了Java这门课,我的期末考试…

  • lodash判断对象数组是否相等_js删除数组中指定元素并返回剩下的

    lodash判断对象数组是否相等_js删除数组中指定元素并返回剩下的使用lodash完成数据处理;毕竟,一般认为,人与动物的本质区别在于制造与使用工具

  • 外键约束

    外键约束外键作用:1.保证数据的完整性、一致性,更可靠。2.设置外键约束的两个表之间会有父子关系,即字表中外键字段的取值范围取决于父表3.设置外键一定程度上降低了数据库的速度4.字表中外键字段的

  • HDU 2896 病毒侵袭 AC自己主动机题解

    HDU 2896 病毒侵袭 AC自己主动机题解

  • 4G技术TDD和FDD分别指什么「建议收藏」

    4G技术TDD和FDD分别指什么「建议收藏」TDD和FDD分别指什么;   TDD(Time Division Duplexing)时分双工技术,在移动通信技术使用的双工技术之一,与FDD相对应。   在TDD模式的移动通信系统中,基站到移动台之间的上行和下行通信使用同一频率信道(即载波)的不同时隙,用时间来分离接收和传送信道,某个时间段由基站发送信号给移动

发表回复

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

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