简介
- 在
javascript
中,将函数作为对象使用时,被称为函数对象;new
函数产生的对象,简称为对象; 而javascript
中的回调函数同样分为了两种: (1)同步回调:立即执行,完全执行完了才会结束,不会放入回调队列中; (2) 异步回调:不会立即执行,会放入回调队列中来执行。promise
就是异步编程的一种解决方案,相对于最初的回调函数更加优雅。最早由社区提出,ES6将其写入了语言标准。
promise是什么
- 通过阅读阮一峰老师的博客,可以看出,
promise
是一个容器,在这个容器中保存着异步操作的结果。语法上来看,promise
是一个对象,可以获取异步操作的消息,promise提供了统一的api处理各种异步操作。
promise特点
-
对象的状态不受外界影响。promsie对象有三种状态:
pending
(进行中),fulfilled
(已成功)和rejected
(已失败)。只有异步操作的结果可以决定当前的状态,不受任何其他因素的影响。 -
一旦改变状态,就不会再变。 promise的状态改变,只有两种可能:
pending
=>fulfilled
和pending
=>rejected
.只有命中其中一种,那么状态就被凝固了。与事件不同的是: 状态改变了,再对promise对象添加回调函数也是会得到这个凝固的值。 - 避免回调地狱(嵌套函数),代码更加优雅;利用promise可以实现链式调用,就可以将异步操作以同步操作的流程表达出,避免回调地狱的产生,并且promise对象也提供统一的接口,可以更加容易的操作异步操作。
promise的缺点
- 无法取消promsie,因为promsie一旦new出来就是会立即执行的,无法中途取消
- 不设置回调函数,无法捕获到promise内部抛出的错误;
- 处于pending阶段的时候,无法得知当前进行的阶段(刚刚开始还是即将完成)
promise基本用法
-
promise
对象是一个构造函数,可以被new
出来,生成promise
实例。const p1 = new Promise(function(resolve, reject) { // 注意: 这里都是同步的代码 if(success) { // 成功的操作 resolve(value) } else { reject(error) } })
-
通过以上的代码可以看出构造函数接受函数为参数,注意该函数是同步函数,会被立即执行;其次,该函数的两个参数分别是resolve和reject,这是两个回调函数,由js引擎提供.
-
resolve主要是实现
pending
=>resolved
;如果异步操作成功,就会触发这个回调函数传递给外部使用。reject
函数是将promise
的状态从pending
=> ·rejected·,如果异步操作失败,那么该函数就会捕获到错误,传递给外部。 -
promise
实例生成以后,可以用then
方法分别指定resolved
和rejected
状态的回调函数,类似于下列代码所示p1.then(function(value) { // 异步的代码 // 成功时,可以在这里做一些操作 }, function(error) => {})
function timeout(ms) { return new Promise((resolve, reject) => { setTimeout(resolve, ms, 'done'); }); } timeout(100).then((value) => { console.log(value); });
-
timeout方法返回一个
Promise
实例,表示一段时间以后才会发生的结果。过了指定的时间(ms参数)以后,Promise
实例的状态变为resolved,就会触发then方法绑定的回调函数 -
·promise一旦被实例后,就会被立即执行
let promise = new Promise(function(resolve, reject) { console.log('Promise'); resolve(); }); promise.then(function() { console.log('resolved.'); }); console.log('Hi!'); // Promise // Hi! // resolved
-
如果调用resolve函数和reject函数时带有参数,那么它们的参数会被传递给回调函数。reject函数的参数通常是Error对象的实例,表示抛出的错误;resolve函数的参数除了正常的值以外,还可能是另一个 Promise 实例,比如像下面这样。
const p1 = new Promise(function (resolve, reject) { // ... }); const p2 = new Promise(function (resolve, reject) { // ... resolve(p1); })
-
p1和p2都是 Promise 的实例,但是p2的resolve方法将p1作为参数,即一个异步操作的结果是返回另一个异步操作。这时p1的状态就会传递给p2,也就是说,p1的状态决定了p2的状态。如果p1的状态是pending,那么p2的回调函数就会等待p1的状态改变;如果p1的状态已经是resolved或者rejected,那么p2的回调函数将会立刻执行。
-
调用resolve或reject并不会终结 Promise 的参数函数的执行,因为立即 resolved 的 Promise 是在本轮事件循环的末尾执行;但是一般来说,
resolve
或者reject
以后,Promise
的使命就完成了,后续操作应该放在then
里面;所以在resovle
之前应该加上return
.
()
-
then
返回的是新的promise实例,所以可以采用链式写法。一个回调函数完成以后,会将返回结果作为参数,传入第二个回调函数。采用链式的then,可以指定一组按照次序调用的回调函数。这时,前一个回调函数,有可能返回的还是一个Promise对象(即有异步操作),这时后一个回调函数,就会等待该Promise对象的状态发生变化,才会被调用。
()
- 用于指定发生错误时的回调函数。如果异步操作抛出错误,状态就会变为rejected,就会调用catch()方法指定的回调函数,处理这个错误
- Promise 对象的错误具有“冒泡”性质,会一直向后传递,直到被捕获为止。也就是说,错误总是会被下一个catch语句捕获。
const someAsyncThing = function() {
return new Promise(function(resolve, reject) {
// 下面一行会报错,因为x没有声明
resolve(x + 2);
});
};
someAsyncThing().then(function() {
console.log('everything is great');
});
setTimeout(() => { console.log(123) }, 2000);
// Uncaught (in promise) ReferenceError: x is not defined
// 123 // 123还是会被执行
- Promise 内部的错误不会影响到 Promise 外部的代码,通俗的说法就是“Promise 会吃掉错误
- 如果promise里还有事件的话,但是promise的运行已经结束的话,那么这个错误就会冒泡
()
- finally()方法用于指定不管 Promise 对象最后状态如何,都会执行的操作。该方法是 ES2018 引入标准的。
- ()方法用于将多个 Promise 实例,包装成一个新的 Promise 实例。
- ()方法接受一个数组作为参数,p1、p2、p3都是 Promise 实例,如果不是,就会先调用下面讲到的方法,将参数转为 Promise 实例,再进一步处理。另外,()方法的参数可以不是数组,但必须具有 Iterator 接口,且返回的每个成员都是 Promise 实例。
- 只有p1、p2、p3的状态都变成fulfilled,p的状态才会变成fulfilled,此时p1、p2、p3的返回值组成一个数组,传递给p的回调函数。
- 只要p1、p2、p3之中有一个被rejected,p的状态就变成rejected,此时第一个被reject的实例的返回值,会传递给p的回调函数
- 会按顺序输出
()
- ()方法同样是将多个 Promise 实例,包装成一个新的 Promise 实例。
- 只要p1、p2、p3之中有一个实例率先改变状态,p的状态就跟着改变。那个率先改变的 Promise 实例的返回值,就传递给p的回调函数。
- 和一样,如果传入的参数不是promise,会使用()将参数转为promise的实例。
()
- ES2020引入, ()方法接受一组 Promise 实例作为参数,包装成一个新的 Promise 实例。
- 只有所有的参数实例都返回结果,包装实例才会结束
- 该方法返回的新的 Promise 实例,一旦结束,状态总是fulfilled,不会变成rejected。状态变成fulfilled后,Promise 的监听函数接收到的参数是一个数组,每个成员对应一个传入()的 Promise 实例。
- 对于不关心操作有没有结束的功能时,就可以用这个。这个可以确保所有的操作都结束了。
()
- ES2021引入,接受一组promise实例作为参数,包装成一个新的promise实例返回,只要有一个变成了
fulfilled
,那么包装实例就会变成fullfilled
状态,如果所有参数都返回了rejected
,那么包装实例就会变成rejected状态 - 不会因为某个promise变成rejected状态而结束
()
- 将现有对象转为 Promise 对象,()方法就起到这个作用。
- 分为四种情况:
- 参数是promise实例,那么不做任何修改,直接返回
- 参数是thenable对象(具有then方法的对象),;会将这个对象转为 Promise 对象,然后就立即执行thenable对象的then()方法。
- 参数不是具有then()方法的对象,或根本就不是对象,会通过这个方法转换成一个新的promise对象,状态时resolved;
- 不带有任何参数:直接返回一个resolved状态的 Promise 对象。立即resolve()的 Promise 对象,是在本轮“事件循环”(event loop)的结束时执行,而不是在下一轮“事件循环”的开始时
()
- (reason)方法也会返回一个新的 Promise 实例,该实例的状态为rejected。
()
- 函数f是同步函数还是异步操作,但是想用 Promise 来处理它。因为这样就可以不管f是否包含异步操作,都用then方法指定下一步流程,用catch方法处理f抛出的错误。一般就会采用下面的写法
- 但是利用来包裹,那么就会在本轮事件循环的末尾执行;除非用async来书写。