ES6 Promise
1》介绍
1.异步编程的一种解决方案
2.简单说就是一个容器,里面保存着某个未来才会结束的事件(通常是一个异步操作)的结果。
从语法上说,Promise 是一个对象,从它可以获取异步操作的消息。
3.特点:
1>.对象的状态不受外界影响。Promise对象代表一个异步操作,有三种状态:pending(进行中)、fulfilled(已成功)和rejected(已失败)。
只有异步操作的结果,可以决定当前是哪一种状态,任何其他操作都无法改变这个状态。
2>Promise对象的状态改变,只有两种可能:从pending变为fulfilled和从pending变为rejected。
只要这两种情况发生,状态就凝固了,不会再变了,会一直保持这个结果,这时就称为 resolved(已定型)。
4.Promise 缺点
1>无法取消Promise,一旦新建它就会立即执行,无法中途取消。
2>其次,如果不设置回调函数,Promise内部抛出的错误,不会反应到外部。
3>第三,当处于pending状态时,无法得知目前进展到哪一个阶段(刚刚开始还是即将完成)。
2》基本用法
1.Promise对象是一个构造函数,用来生成Promise实例;构造函数的含有两个函数参数 resolve与reject
resolve函数的作用是,将Promise对象的状态从“未完成”变为“成功”(即从 pending 变为 resolved),在异步操作成功时调用,并将异步操作的结果,作为参数传递出去;
reject函数的作用是,将Promise对象的状态从“未完成”变为“失败”(即从 pending 变为 rejected),在异步操作失败时调用,并将异步操作报出的错误,作为参数传递出去。
var promise = new Promise(function(resolve, reject) {
// ... some code
if (/* 异步操作成功 */){
resolve(value);
} else {
reject(error);
}
});
2.Promise实例生成以后,可以用then方法分别指定resolved状态和rejected状态的回调函数。
then方法可以接受两个回调函数作为参数。
第一个回调函数是Promise对象的状态变为resolved时调用,第二个回调函数是Promise对象的状态变为rejected时调用。第二个函数是可选的,不一定要提供。
这两个函数都接受Promise对象传出的值作为参数。即调用resolve函数和reject函数时带有参数,那么它们的参数会被传递给回调函数。
promise.then(function(value) {
// resolve
}, function(error) {
// reject
});
function timeout(ms) {
return new Promise(function(resolve,reject){
setTimeout(resolve('done'), ms);
})
}
timeout(100).then(function(value){
console.log(value)
});
3.Promise 新建后就会立即执行,所以首先输出的是Promise。然后,then方法指定的回调函数,将在当前脚本所有同步任务执行完才会执行,所以resolved最后输出。
var promise = new Promise(function(resolve, reject) {
console.log('Promise');
resolve();
});
promise.then(function() {
console.log('resolved');
});
console.log('Hi!'); //Promise Hi! resolved.
4.调用resolve或reject并不会终结 Promise 的参数函数的执行。因为立即 resolved 的 Promise 是在本轮事件循环的末尾执行,总是晚于本轮循环的同步任务。
new Promise((resolve, reject) => {
resolve(1);
console.log(2);
}).then(r => {
console.log(r);
}); //2 1
改进
new Promise((resolve, reject) => {
return resolve(1);
// 后面的语句不会执行
console.log(2);
})
3》Promise.prototype等方法
1.Promise.prototype.then()
链式的then,可以指定一组按照次序调用的回调函数。
这时,前一个回调函数,return有可能返回的还是一个Promise对象(即有异步操作),这时后一个回调函数,就会等待该Promise对象的状态发生变化,才会被调用。
promise.then().then()....
2.Promise.prototype.catch()
<1>Promise.prototype.catch方法是.then(null, rejection)的别名,用于指定发生错误时的回调函数。
const promise = new Promise(function(resolve, reject) {
throw new Error('test');
});
promise.catch(function(error) {
console.log(error);
});
<2>如果 Promise 状态已经变成resolved,再抛出错误是无效的。
const promise = new Promise(function(resolve, reject) {
resolve('ok');
throw new Error('test');
});
promise.then(function(value) { console.log(value) }).catch(function(error) { console.log(error) });
<3>Promise 对象的错误具有“冒泡”性质,会一直向后传递,直到被捕获为止。也就是说,错误总是会被下一个catch语句捕获。
promise.then(function(post) {
return otherpromise;
}).then(function(comments) {
// some code
}).catch(function(error) {
// 处理前面三个Promise产生的错误
});
<4>Promise 内部的错误不会影响到 Promise 外部的代码,通俗的说法就是“Promise 会吃掉错误”。
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);
3.Promise.prototype.finally()
finally方法用于指定不管 Promise 对象最后状态如何,都会执行的操作;且finally方法的回调函数不接受任何参数
promise.then(result => {···}).catch(error => {···}).finally(() => {···});
实现:https://github.com/matthew-andrews/Promise.prototype.finally/blob/master/finally.js
4.Promise.all()
用于将多个 Promise 实例,包装成一个新的 Promise 实例;接受一个数组作为参数 Promise.all([p1, p2, p3]);
区别:并行执行异步操作的能力,并且在所有异步操作执行完后才执行回调
https://github.com/Advanced-Frontend/Daily-Interview-Question/issues/130
Promise.all([runAsync1(), runAsync2(), runAsync3()]).then(function(results){
console.log(results);
});
5.Promise.race()
用于将多个 Promise 实例,包装成一个新的 Promise 实例接受一个数组作为参数 Promise.all([p1, p2, p3]);
all方法的效果实际上是「谁跑的慢,以谁为准执行回调」,那么相对的就有另一个方法「谁跑的快,以谁为准执行回调」
https://github.com/Advanced-Frontend/Daily-Interview-Question/issues/140
function runAsync1(){
var p = new Promise(function(resolve, reject){
//做一些异步操作
setTimeout(function(){
console.log('异步任务1执行完成');
resolve('随便什么数据1');
}, 1000);
});
return p;
}
function runAsync2(){
var p = new Promise(function(resolve, reject){
//做一些异步操作
setTimeout(function(){
console.log('异步任务2执行完成');
resolve('随便什么数据2');
}, 2000);
});
return p;
}
function runAsync3(){
var p = new Promise(function(resolve, reject){
//做一些异步操作
setTimeout(function(){
console.log('异步任务3执行完成');
resolve('随便什么数据3');
}, 2000);
});
return p;
}
Promise.all([runAsync1(), runAsync2(), runAsync3()]).then(function(results){
console.log(results);
});
Promise.race([runAsync1(), runAsync2(), runAsync3()]).then(function(results){
console.log(results);
});
6.Promise.resolve()
将现有对象转为 Promise 对象,Promise.resolve方法就起到这个作用。
(1)参数是一个 Promise 实例
如果参数是 Promise 实例,那么Promise.resolve将不做任何修改、原封不动地返回这个实例。
(2)参数是一个thenable对象
Promise.resolve方法会将这个对象转为 Promise 对象,然后就立即执行thenable对象的then方法。
thenable对象指的是具有then方法的对象,比如下面这个对象。
let thenable = {
then: function(resolve, reject) {
resolve(42);
}
};
let p1 = Promise.resolve(thenable);
p1.then(function(value) {
console.log(value); // 42
});
(3)参数不是具有then方法的对象,或根本就不是对象
如果参数是一个原始值,或者是一个不具有then方法的对象,则Promise.resolve方法返回一个新的 Promise 对象,状态为resolved。
const p = Promise.resolve('Hello');
p.then(function (s){
console.log(s)
});// Hello
(4)不带有任何参数
Promise.resolve方法允许调用时不带参数,直接返回一个resolved状态的 Promise 对象。
需要注意的是,立即resolve的 Promise 对象,是在本轮“事件循环”(event loop)的结束时,而不是在下一轮“事件循环”的开始时。
setTimeout(function () {
console.log('three'); //下一轮“事件循环”开始时执行
}, 0);
Promise.resolve().then(function () {
console.log('two'); //本轮“事件循环”结束时执行
});
console.log('one'); // one two three
7.Promise.reject()
Promise.reject(reason)方法也会返回一个新的 Promise 实例,该实例的状态为rejected。
参数用法同上
const p = Promise.reject('出错了');
// 等同于
const p = new Promise((resolve, reject) => reject('出错了'))
8.Promise.try()
让同步函数同步执行,异步函数异步执行
4》案例:
案例1:
const promise = new Promise((resolve, reject) => { console.log(1); resolve(); console.log(2); }) promise.then(() => { console.log(3); }) console.log(4);
首先 Promise 新建后立即执行,所以会先输出 1,2,而 Promise.then() 内部的代码在 当次 事件循环的 结尾 立刻执行 ,所以会继续输出4,最后输出3。
案例2:
const promise = new Promise((resolve, reject) => { resolve('success1'); reject('error'); resolve('success2'); }); promise.then((res) => { console.log('then:', res); }).catch((err) => { console.log('catch:', err); })
resolve 函数将 Promise 对象的状态从“未完成”变为“成功”(即从 pending 变为 resolved),在异步操作成功时调用,并将异步操作的结果,作为参数传递出去;
reject 函数将 Promise 对象的状态从“未完成”变为“失败”(即从 pending 变为 rejected),在异步操作失败时调用,并将异步操作报出的错误,作为参数传递出去。
而一旦状态改变,就不会再变。所以 代码中的reject('error'); 不会有作用。
Promise 只能 resolve 一次,剩下的调用都会被忽略。所以 第二次的 resolve('success2'); 也不会有作用。因此:then: success1
案例3:
Promise.resolve(1) .then(2) .then(Promise.resolve(3)) .then(console.log)
Promise.resolve 方法的参数如果是一个原始值,或者是一个不具有 then 方法的对象,则 Promise.resolve 方法返回一个新的 Promise 对象,状态为resolved,Promise.resolve 方法的参数,会同时传给回调函数。
then 方法接受的参数是函数,而如果传递的并非是一个函数,它实际上会将其解释为 then(null),这就会导致前一个 Promise 的结果会穿透下面。因此:1