promise
- 是异步编程的一种解决方案(比传统的回调函数更加合理、强大),用同步操作将异步流程表达出来。避免层层嵌套回调。promise 对象提供统一接口,使得控制异步操作更加容易。
- promise 是一个容器,里面保存着一个事件(在某个未来才会有结果)的结果。
- 从语法上讲 promise 是一个对象,这个对象有以下两个特点:
- 对象状态不受外界影响,有三种状态:pending、fulfilled、rejected。只有异步操作结果可以改变这个状态,任何其他操作都无法改变这个操作。这也是 promise 名字的由来,'承诺',表示其他手段无法改变。
- 状态一旦改变,就不会在变,任何时候都可以得到这个结果。状态只能由 pending => fulfilled、pending => rejected。 只要这两种情况发生了,就凝固了,会一直保持。这时就称 resolved (已定型)。 如果状态已经发生改变,我们在对promise 对象添加回调函数,也会立即得到这个结果。 这与事件(event)完全不同,事件的特点是,你错过了他,再去监听,是得不到结果的。
- 缺点:
- 无法取消promise, promise一旦新建,就会立即执行,无法中途取消。
- 如果不设置回调函数, Promise 内部抛出的错误,无法反应到外部。
- 当处于 pending 状态的时候,无法得知目前进展到哪一个阶段(刚开始还是即将完成)
- Peomise 对象是一个构造函数,用来生成 Promise实例。
const promise = new Promise(function(resolve, reject){
if (res) {
resolve(value) // 异步操作成功,翻转状态:pending => fulfilled,并且返回响应的值 Promise的实例状态变为 resolved,就会出发 then 方法。
console.log('11111') // resolve 、reject 不会终止 Promise 的参数函数的执行,这里的 11111 仍然会打印出来,并且会先于 resolve(res) 执行,因为立即 resolved 的 Promise 是在本轮事件循环的末尾执行,总是晚于本轮循环的同步。resolved 仍然是一个 promise,promise创建的是微任务,console.log('11111') 是在 script 的创建的 宏 任务列表中的。 Promise 构造函数的参数函数是立即执行的,但是 resolved 返回的新的 promise 是微任务。
} else {
reject() // // 异步操作失败,pending => rejected,返回错误失败原因
console.log('22222')
}
})
promise.then(res => {
// resolved 成功 执行的回调函数
}, error => {
// rejected 失败 执行的回调函数
})
=== 等价于,推荐使用下面的方式,更直观,还能捕获错误
promise.then(res => {
// resolved 成功 执行的回调函数
}).catch(error => {
// rejected 失败 执行的回调函数,这里这个 catch 不仅会捕获 rejected 的失败原因,如果 .then 成功回调函数的代码执行有错误,也会进入到catch,捕获到错误。
})
- Promise.prototype.then((res) => {}) 只有Promise 的状态为 resolved 才会进入这个回调
- Promise.prototype.catch((error) => {} ) 只有Promise 的状态为 rejected 才会进入这个回调
- Promise.prototype.finally(()=> {}) 这个方法不接受任何参数,不管前面的 Promise 状态是 resolved 还是 rejected,都会执行finally 里面的回调.
- Promise.all()方法将多个 Promise 实例,包装成一个新的Promise 实例。 接受一个数组作为参数,每个数组的元素都是 Promise 的实例,如果不是,会先调用 Promise.resolve()方法,将参数转为 Promise 实例。参数也可以不是数组,但是必须具有 Iterator 接口,且返回的每个成员都是 Promsie 实例。 只有当所有的参数 的状态都是 fulfilled ,Promise.all() 的状态才会变成 fulfilled,此时 数组参数的返回值组成一个数组,传递给 Promise.all() 的回调函数。 数组参数中只要有一个被 rejected, Promise.all() 的状态就变成了 rejected,此时第一个被 reject 的实例返回值会传递给 Promise.all() 的回调函数。 如果其中一个参数定义了自己的 catch 回调,即使其被 rejected,那么 Promise.all() 的 catch 方法与不会被触发。
- Promise.race() 方法将多个 Promise 实例包装成一个新的 Promise 实例。其参数和 Promise.all() 方法一致。 const p = new Promise([p1,p2,p3]); 只要p1,p2,p3中又一个率先改变状态,那么 p 的状态就会跟着改变,其状态和 p 的状态一摸一样。
- Promise.allSettled() 方法将多个 Promise 实例包装成一个新的 Promise 实例。只有等到所有参数实例都有返回结果,不管是 fulfilled 还是 rejected,包装实例才会结束。该方法返回一个新的 Promise 实例,一旦结束,状态总是 fulfilled,不会变成 rehected。一旦状态变成 fulfilled 后,Promsie 的监听函数接收到的参数是一个数组,每个成员对应一个传入 Promise.allSettled() 的 Promise 实例。Promise.allSettled() 的监听函数 then(result => {}) 接收到的参数 result 数组形式为:
[
{ status: 'fulfilled', value: '42' }, // 返回值为 fulfilled 成功的数据项
{ status: 'rejected', reason: -1 } // 返回值为 rejected 失败的数据项
]
这个函数不关心 异步操作的结果,侧重于关心所有的异步操作有没有结束。
- Promise.any() 该方法接收一组 Promise 实例作为参数,包装成一个新的 Promise 实例返回。参数实例中只要又一个状态变成 fulfilled 状态,包装实例就会变成 fulfilled 状态,如果所有参数实例都变成 rejected 状态,包装实例才会变成 rejected 状态。如果三个参数实例都变成 rejected,那么 promise.any() 就会抛出错误,这个错误不是一般的错误,而是一个 AggregateError 实例,它相当于一个数组,对应的是每一个 被 rejected 的操作所抛出的错误。
- Promise.resolve() 将对象转为 Promise 对象。 Promise.resolve('foo') === new Promise(resolve => resolve('foo'))
Promise.resolve() 参数有四种情况: - 参数 是一个 Promise 实例,那么 Promise.resolve() 不做任何处理,原封不动的返回这个实例。
- 参数是一个 thenable 对象。 thenable 对象指的是具有 then 方法的对象。
let thenable = {
then(resolve, reject) {
console.log('0')
resolve(42)
}
}
let p1 = Promise.resolve(thenable)
p1.then(function(value) {
consoel.log(val)
})
Promise.resolve(thenable) 的时候会立即调用 thenable 的 then 方法,执行后p1 的状态就变为 resolved, 从而立即执行最后那个 p1.then() 方法的回调函数,输出42.
首先打印出 '0', 然后才是 42
Promise.resolve() 的参数是一个 thenable 对象,Promise.resolve() 会将这个 对象转为 Promise 对象,并且随后会立即执行 thenable 对象的 then 方法。
3. 参数不具有 then 方法,或根本不是对象。 如果参数是一个原始值,或者不具有 then() 的对象,则 Promise.resolve() 方法返回一个新的 Promsie 对象,状态为 resolved。
const p = Promise.resolve('hello');
p.then(function(res){
console.log(res); // hello
})
由于 hello 不是异步操作(非 thenable 对象), Promise.reolsve() 操作处理之后返回的 Promise 实例状态就是 resolved,所以实例P 的回调函数会立即执行。同时 Promise.resolve() 方法的参数会同时传递给回调函数。
4. 不带有任何参数。
Promise.resolve() 方法允许调用时候不带参数,直接返回一个 resolved 状态的 Promise 实例。
Promise.reject() 方法返回一个新的 Promsie 实例,该实例的状态是 rejected. Promise.reject() 的参数,会作为 reject 的理由,变成后续方法 catch 的参数。
Promise.try()
不知道 或者 不想区分 函数f 是同步还是异步函数,但是想用Promsie 来处理它,因为这样就可以不管f 是否包含异步操作,都用 then 方法指定下一步流程,用catch 方法处理 f 抛出的错误。
const f = () => console.log('now');
Promise.resolve().then(f);
consoel.log('next')
// 这样处理 f 就变成了异步函数。 打印顺序:next now
思考:有没有一种方式:让同步函数同步执行,异步函数异步执行。并且让它们具有统一的API。
1. 第一种方法: 使用 async 函数来写。需要注意 async () => f() 会吃掉f() 抛出的错误,如果想要抛出错误,需要使用 promise.catch 方法。
(async ()=> f())()
.then(...)
.catch(...)
2. 第二种方法:使用 new Promise()
cosnt f = () => console.log('now')
(new Promise(
resolve => resolve(f)
)()
console.log('next');
上面代码也是使用立即执行的匿名函数,执行new Promise()。这种情况下,同步函数也是同步执行的。 输出顺序: now next
Promise.try(callback)
.then(...)
.catch(...)
统一的、更加简洁的:API。 catch可以捕获 callback 的错误,也可以捕获 then 中的错误。
等价于 ===
try {
callback()
.then(...)
.catch(...)
} catch(...)
这里需要两个 catch 原因是: 异步函数 callback 的catch 不能捕获callback自身的错误(etc: 链接数据库)。所以需要 try {} catch {}