promise是JS的一种异步编程解决方案。setTimeout和Ajax都是常见的异步回调方案,但是他们都有一个问题,就是对于复杂的回调会产生层层嵌套的问题,代码会像一个横放的金字塔意一样一层套一层,编码体验极差。promise的多重链式回调可以很好的解决这个问题。
首先promise对象代表的是现在未完成,但是将来会完成的操作。它一共有三个状态:
var promise = new promise(function (resolve,reject) {
}
promise的参数为函数,resolve是一部操作执行成功时执行的函数,reject是异步操作执行失败时执行的函数。
1.then()
.then()方法可以指定resolve和reject状态下回调的函数
promise.then(function (data) {
//操作成功状态执行的内容
},function (error) {
//操作失败状态执行的内容
})
2.catch()
.catch()方法用于指定发生错误时的回调函数,可以看作是用来捕获错误的。虽然用.then()方法就可以指定reject状态下的回调函数,但是promise对象抛出的错误不会传递到外层代码,浏览器就不会抛错(除了chrome)。
promise.catch(function (error) {
})
3.all()
.all()方法的作用是将多个promise实例包装成一个新的promise实例,且只有所有promise实例状态都变成fulfilled,新的promise实例状态才会变成fulfilled。
只要有一个是rejected,新实例的状态就会变成rejected。
var p1 = new Promise(function (resolve, reject) {
setTimeout(resolve, 3000, "first");
});
var p2 = new Promise(function (resolve, reject) {
resolve('second');
});
var p3 = new Promise((resolve, reject) => {
setTimeout(resolve, 1000, "third");
});
Promise.all([p1, p2, p3]).then(function(values) {
console.log(values);
});
//约 3s 后
["first", "second", "third"]
注意p1,p2,p3是同步执行的
4.race()
.race()的作用基本和.all()一样,不同之处在于p1,p2,p3中有一个状态变成fulfilled或者rejected,P的状态就会随之改变,并向回调函数中传递一个改变的状态。
5.resolve()
.resolve()方法可以立即让promise进入resolved状态,并将success结果传递给.then()。
.resolve()方法返回的是promise对象,可以用.then()处理返回值。
6.reject()
让promise立即进入rejected状态
const promise = new Promise((resolve, reject) => {
console.log(1);
resolve();
console.log(2);
})
promise.then(() => {
console.log(3);
})
console.log(4);
答案:1 2 4 3
思路:.then()是异步的
setTimeout(function(){
console.log(4)
},0);
new Promise(function(resolve){
console.log(1)
for( var i=0 ; i<10000 ; i++ ){
i==9999 && resolve()
}
console.log(2)
}).then(function(){
console.log(5)
});
console.log(3);
答案:1 2 3 5 4
思路:涉及到js的执行队列,js是单线程多队列的
第一步:首先创建一个macrotask,里面放入整个scrpipt。执行过程中会创建一个新的macrotask,里面放入setTimeout()。
在创建执行第一个macrotask的同时还创建了一个microtask,里面放入了promise.nextTick和console。log(3),执行之后输出了1和2
第二步:根据microtask的特性它会连续执行直到队列清空,于是接下去执行console.log(3),然后执行promise.then(),输出3和5。
第三步:最后执行macrotask中的setTimeout(),输出4
Promise.resolve(1)
.then(2)
.then(Promise.resolve(3))
.then(console.log)
结果:1
思路:promise.then()期望传入的参数是函数,传入非函数会发生值穿透。
const promise = new Promise((resolve, reject) => {
setTimeout(() => {
console.log('开始');
resolve('success');
}, 5000);
});
const start = Date.now();
promise.then((res) => {
console.log(res, Date.now() - start);
});
promise.then((res) => {
console.log(res, Date.now() - start);
});
结果:开始,sucess 5002,success 5002
思路:promise.then()可以调用多次,但是里面的构造函数值执行一次,它执行一次之后值就被储存了,接下去每次调用都是直接拿值。
5.实现一个promise
//先创建一个promise类,搭好架子
class MyPromise {
//构造一个构造方法用来检测传入参数和初始化promise
constructor(fn){
if(typeof fn !== 'function') {
throw new TypeError(`MyPromise fn ${fn} is not a function`)
} //如果传入的不是函数那就抛出错误
this.state = 'pending';
this.value = void 0;
fn(this.resolve.bind(this),this.reject.bind(this)) //改变this的指向,使下面俩方法能修改state和value的值
}
//用于改变state和value的值
resolve(value){
if(this.state !== 'pending') return;
this.state = 'fulfilled';
this.value = value
}
reject(reason){
if(this.state !== 'pending') return;
this.state = 'rejected';
this.value = reason
}
//回调方法
then(fulfilled,rejected){
//判断传入的是不是函数,如果不是直接跳过,返回this进行下一步,这可以解释第三题为什么会返回1.
if (typeof fulfilled !== 'function' && typeof rejected !== 'function' ) {
return this;
}
//判断是不是多次.then()调用,如果时,不执行函数直接返回值,这可以解释第四题。
if (typeof fulfilled !== 'function' && this.state === 'fulfilled' ||
typeof rejected !== 'function' && this.state === 'rejected') {
return this;
}
//then返回的是一个新的promise对象
return new MyPromise((resolve,reject)=>{
if(fulfilled && typeof fulfilled === 'function' && this.state === 'fulfilled'){
let result = fulfilled(this.value); //调用.then()中传入的fulfilled方法
//判断是否有链式调用,如果result存在且result.then是一个function则将返回值传入下一个promise
if(result && typeof result.then === 'function'){
return result.then(resolve,reject)
}else{
//如果是一个普通值就直接resolve()
resolve(result)
}
}
if(rejected && typeof rejected === 'function' && this.state === 'rejected'){
let result = rejected(this.value); //调用.then()中传入的rejected方法
if(result && typeof result.then === 'function'){
return result.then(resolve,reject)
}else{
resolve(result)
}
}
})
}
catch(rejected){
return this.then(null,rejected)
}
}