Promise是异步编程的一种解决方案。ES6提供原生支持,并为Promise提供了统一的 API。
从语法上说,Promise是一个JS的原生对象,从它可以获取异步操作的消息。
Promise 本身是一个构造函数,可以像下面这样构造一个Promise实例
javascriptconst p = new Promise((resolve, reject) => {
resolve('success');
});
console.log(p);
打印结果如下
Promise实例有两个私有属性,其中[[PromiseStates]]
代表状态,一共有三种状态,[[PromiseValue]]代表结果。
[[PromiseStates]]
[[PromiseValue]]
基本介绍就写这么多了,网上资料太多了。
下面主要是对一些特性的试验,基本以代码为主。
下面是我自己使用下来总结的一些特点,不具有代表性。
所谓立即执行性,是指Promise 构造函数新建实例后立即执行,它也是同步代码。
所谓异步性,是指promise 的then()
具有异步性,当执行到.then()
部分,这部分会自动进入到Promise的异步事件队列,不会阻塞同步代码的执行。需要注意的是promise这种异步任务是属于一个微任务,它和以setTimeout为代表的宏任务在执行顺序上有一点区别。
javascriptconst p = new Promise((resolve, reject) => {
console.log('promise内部')
resolve('success');
});
p.then((value) => {
console.log('then内部', value);
});
console.log('主程序');
输出结果
javascriptpromise内部 主程序 then内部 success
javascriptconst p1 = new Promise((resolve, reject) => {
resolve('p1 success-1');
resolve('p1 success-2');
});
const p2 = new Promise((resolve, reject) => {
resolve('p2 success');
reject('p2 reject');
});
const p3 = new Promise((resolve, reject) => {
reject('p3 reject');
resolve('p3 success');
});
p1.then((value) => {
console.log(value);
});
p2.then((value) => {
console.log(value);
}, (err) => {
console.log(err);
});
p3.then((value) => {
console.log(value);
}, (err) => {
console.log(err);
});
输出结果
javascriptp1 success-1
p2 success
p3 reject
Promise一旦使用了resolve或者reject的时候,状态就不能再次变化,这就是Promise的不可逆性。
jsconst p = new Promise(((resolve, reject) => {
resolve(5);
}));
const p1 = p.then((value) => {
console.log(value);
return value * 10;
})
console.log(p1)
输出结果
可以看到then
返回的就是一个Promise,而且是立即返回。这边之所以展开是resolved状态,是因为console.log的异步性,或者说在展开对象属性之前,它保留的是一个对象的引用。如果需要实时查看状态,加个断点即可。
也正是因为then
返回的是一个新的Promise,所以可以通过链式调用then
方法。
then
方法接收两个函数作为参数,第一个参数是Promise执行成功时的回调,第二个参数是Promise执行失败时的回调。两个函数只会有一个被调用,函数的返回值将被用作创建then返回的Promise对象。
大概总结了下面四种情况
return
了一个同步的值,经过测试,不管返回null,undefined或者其他类型,then
方法将返回一个resolved状态的Promise对象,Promise对象的值就是这个返回值。
javascriptconst p = new Promise(((resolve, reject) => {
resolve(1);
}));
p
.then((value) => {
return null;
})
.then((value) => {
console.log(value);
console.log('对象类型', {}.toString.call(value));
})
输出结果
javascriptnull
对象类型 [object Null]
没有return
任何值
输出结果
javascriptundefined
对象类型 [object Undefined]
return
了另一个 Promise,then
方法将根据这个Promise的状态和值创建一个新的Promise对象返回。
比如返回的是Promise.resolved(value)
,则Promise的状态为resolved,值为value。
javascriptconst p1 = new Promise(function (resolve, reject) {
resolve(1);
})
const p2 = p1.then((value) => {
return p1;
})
p2.then((value) => {
console.log(value)
})
console.log(p1 === p2)
输出结果
javascriptfalse
1
throw
一个同步异常,then
方法将返回一个Promise,状态是rejected,值是throw
的参数。
javascriptconst p1 = new Promise(function (resolve, reject) {
setTimeout(() => {
console.log('p1-promise');
reject('这个是错误')
}, 3000)
});
const p2 = new Promise(function (resolve, reject) {
console.log('p2-promise');
resolve(p1);
})
p2
.then(result => console.log('then内部', result))
.catch(error => console.log('catch内部', error))
输出结果
javascriptconst p1 = new Promise(function (resolve, reject) {
setTimeout(() => {
console.log('p1-promise');
resolve('这个是错误')
}, 3000)
});
const p2 = new Promise(function (resolve, reject) {
console.log('p2-promise');
reject(p1);
})
p2
.then(result => console.log('then内部', result))
.catch(error => console.log('catch内部', error))
输出结果
javascriptconst p1 = new Promise(function (resolve, reject) {
setTimeout(() => {
console.log('p1-promise');
reject('这个是错误')
}, 3000)
});
const p2 = new Promise(function (resolve, reject) {
console.log('p2-promise');
reject(p1);
})
p2
.then(result => console.log('then内部', result))
.catch(error => console.log('catch内部', error))
输出结果
三个例子的结果各异,对于这样的现象,我个人的理解是这样。
p1和p2都是一个Promise。一方面,在p2代码中,如果使用reject方法,p2的状态变成了一个rejected,值就是p1,错误直接被catch住,而且error本身就是p1。三秒以后,如果p1的状态变成了resolved,可以添加then
添加回调获取p1的值,p1的状态变成了rejected,则就抛出一个错误了。
另一方面,在p2代码中,如果使用resolve方法,p2的状态不会马上变成resolved,但是这个时候,它会依赖p1的结果,所以它会等到三秒以后p1的状态,如果p1变成了resolved,则会执行then
中的代码,如果p1变成了rejected,则会执行catch
中的代码,并且很重要的一点是它会对p1进行类似拆箱的操作,会直接拿到p1的结果,作为then
或者catch
回调的参数。
javascriptconst p1 = Promise.resolve(1);
const p2 = Promise.resolve(p1);
const p3 = new Promise((resolve, reject) => {
resolve(1);
});
const p4 = new Promise((resolve, reject) => {
resolve(p1);
});
console.log(p1 === p2);
console.log(p1 === p3);
console.log(p3 === p4);
输出结果
javascripttrue
false
false
p1接收了一个普通值1,所以会返回一个resolved状态的Promise对象,值为1。
p2接收了一个Promise对象p1,会直接返回这个Promise对象。
p3和p4通过new
方式创建了一个新的Promise对象,所以p3和p1,p4都不会相等。
javascriptconst p = new Promise(((resolve, reject) => {
reject('promise内部的错误');
}));
p
.then((value) => {
console.log(value);
return value * 10;
})
setTimeout(() => {console.log('主程序')}, 1000)
如果Promise抛出一个错误,但是在then
中没有第二个参数来捕获这个错误的话,就会在控制台打印错误信息,但是不会阻塞代码继续执行。
javascriptconst p = new Promise(((resolve, reject) => {
resolve(1);
}));
p
.then((value) => {
console.log('第一个then的第一个回调', value);
return Promise.reject('then中错误啦')
}, (value) => {
console.log('第一个then的第二个回调', value);
})
.then((value) => {
console.log('第二个then的第一个回调', value);
}, (value) => {
console.log('第二个then的第二个回调', value);
})
.then((value) => {
console.log('第三个then的第一个回调', value);
}, (value) => {
console.log('第三个then的第二个回调', value);
}).catch(error => console.log('catch内部', error))
输出结果
javascript第一个then的第一个回调 1
第二个then的第二个回调 then中错误啦
第三个then的第一个回调 undefined
因为在第二个then
的参数中有对错误的处理,所以它可以捕获之前的error信息,并且自身的状态也变成了resolved。所以它能执行第三个then
的回调。
如果不在then
中进行对之前Promise的捕获,则一旦发生错误,会中断Promise链后面的代码,直接被catch
到错误。
javascriptconst p = new Promise(((resolve, reject) => {
// throw new Error('promise内部的错误')
resolve(1);
}));
p
.then((value) => {
console.log('第一个then的第一个回调', value);
return Promise.reject('then中错误啦')
})
.then((value) => {
console.log('第二个then的第一个回调', value);
})
.then((value) => {
console.log('第三个then的第一个回调', value);
}).catch(error => console.log('catch内部', error))
输出结果
javascript第一个then的第一个回调 1
catch内部 then中错误啦
如果在Promise中直接throw了一个错误的话,则会让Promise的状态变成rejected,不会直接Promise下面的代码。
javascriptconst p = new Promise(((resolve, reject) => {
throw new Error('promise内部的错误')
resolve(1);
}));
p
.then((value) => {
})
.catch(error => console.log('catch内部', error))
输出结果
javascriptcatch内部 Error: promise内部的错误
at Promise ((index):38)
at new Promise (<anonymous>)
at (index):37
在Promise代码外部使用try catch
并不会得到想要的结果。因为try catch
捕获的是同步代码中的错误。
关于更多捕获在异步代码中错误的问题,可以参考我的另一篇日记。
如何让Promise顺序执行,可以利用async和await。下面是一个很简单的demo。
javascriptlet p1 = () => new Promise(resolve => {
setTimeout(() => resolve(1), 1000)
})
let p2 = () => new Promise(resolve => {
setTimeout(() => resolve(2), 2000)
})
let p = [p1, p2];
async function queue() {
for (let i=0; i<p.length; i++){
let result = await p[i]();
console.log(result)
}
}
queue()
简单封装一下ajax
javascriptconst fetchData = function(url, method, headerConfig) {
return new Promise(function(resolve, reject){
const handleChange = function() {
if (this.readyState !== 4) {
return;
}
if (this.status === 200 || this.status === 304) {
resolve(this.response);
} else {
reject(new Error(this.statusText));
}
};
const xhr = new XMLHttpRequest();
xhr.open(method, url);
xhr.onreadystatechange = handleChange;
xhr.responseType = "json";
if (typeof headerConfig === 'object' && headerConfig !== null) {
for (let headerKey in headerConfig) {
xhr.setRequestHeader(headerKey, headerConfig[headerKey]);
}
}
xhr.send();
});
};
本文作者:sora
本文链接:
版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!