promise相关
Promise是异步编程的一种解决规范,从语法上讲promise是一个对象,从它可以获取异步操作的消息;从本意来讲,它是承诺,承诺过一段时间给你一个结果。promise有三种状态pending(等待)、fulfilled(成功)、rejected(失败),状态一旦改变,就不会再变,创建promise示例后,会立即回调种的内容执行。
promise的用处:
- 回调地狱,代码难维护,常常第一个的函数的输出是第二个函数的输入这种现象。
- promise可以支持多个并发的请求,获取并发请求中的数据。
面试题
return Error
Promise.resolve()
.then(res => {
console.log('a');
return new Error('error');
})
.then(res => {
console.log('b');
console.log('then: ', res);
})
.catch(res => {
console.log('c');
console.log('catch: ', res);
})
输出结果:
- a
- b
- then: Error error
解析:then 中return 一个Error,并不会进入catch ,而是 fulfilled状态,会按then的逻辑往下走
then回调函数的返回
const promise = Promise.resolve()
.then(res => {
return promise;
})
输出结果:执行这个段会报错,Uncaught (in promise) TypeError: Chaining cycle detected for promise #<Promise>
解析:在then中返回起自身,会导致死循环
then的参数是函数
Promise.resolve(1)
.then(2)
.then(Promise.resolve(3))
.then(console.log);
输出: 1
解析:then的参数是函数,第一个then 和 第二个then的参数都不是函数,即无效,所以最后输出结果是 1
then的第二个参数
Promise.resolve()
.then(
sucess => { throw new Error('error')},
fail => { console.log('fail: ', fail) }
)
.catch(
fail2 => { console.log('fail2: ', fail2) }
)
输出:fail2: Error error
解析:then可以接受两个函数作为参数,一个为resolve时调用,一个rejected是调用,两个函数一定只有一个会被调用;catch中的函数,可以监控到前面(包括then)中的异常。
catch 和 then
Promise.reject('error')
.then(res => {
console.log('then1: ', res);
throw new Error('erro1');
})
.catch(err => {
console.log('catch1: ', err);
return 'catch1'
})
.then(res => {
console.log('then2: ', res);
return new Error('then2: error');
})
.catch(err => {
console.log('catch2: ', err)
})
输出:
- catch1: error
- then2: catch1
解析:第一个then是不会进入的,会进入第一个cath,执行里面的函数,然后进入第二个then,执行其中的语句,但是Error是return的,而不是throw,所以不会进入第二个catch
执行队列1
new Promise((resolve, reject) => {
console.log(1);
resolve();
console.log(2);
})
.then(() => {
console.log(3);
})
console.log(4);
输出: 1 2 4 3
解析:new Promise的回调函数回同步执行,所以会首先输出 1 2,resolve的需要 需要进入下一个事件循环,所以最后输出3;
执行队列2
const promise = new Promise((resolve, reject) => {
setTimeout(() => {
resolve(1);
}, 3000);
});
// 下面三种的区别
promise.then(() => {
return Promose.resolve(2);
})
.then(res => { console.log(res) });
promise.then(() => {
return 2;
})
.then(res => { console.log(res) });
promose.then(2).then(res => {
console.log(res);
})
第一个:输出 2,需要等5次事件循环,才会走到then,然后输出结果
第二个:输出 2,需要3次循环
第三个:输出 1,then接受的参数是函数,无参数是,会将前面的结果透传
执行队列3
let a;
const b = new Promise((resolve, reject) => {
console.log('promise1');
resolve();
}).then(() => {
console.log('promise2');
}).then(() => {
console.log('promise3');
}).then(() => {
console.log('promise4');
});
a = new Promise(async(resolve, reject) => {
console.log(a)
await b;
console.log(a);
console.log('after1');
await a;
resolve();
console.log('after2');
});
console.log('end');
输出:
promise1
undefined
end
promise2
promise3
promise4
Promise(pending)
after1
解析:代码从往下执行,Promise的回调函数同步执行,所以 首先 输出【promise1】,然后then放入下一个事件队列。接下执行第二个Promise,输出a的定义,此时a 还没有被赋值,所以输出【undefined】,然后等待b返回结果(异步),继续执行当前的事件队列,输出【end】;执行第二个事件队列,加上a中await b,所以接下来会输出【promise2 promise3 promise4】,然后执行await b 后面的内容,输出【after1】
after2没有输出的原因是在前面的a的已经是Promise对象,且状态为pending,指向到await a的时候,需要等a有返回值后才会执行后续的,所以await a后续代码一直没有执行。
Promise的状态只能修改一次
new Promise((resolve, reject) => {
resolve('success');
reject('fail');
resolve('error');
}).then(res => {
console.log(res);
}).catch(res => {
console.log(res);
});
输出:success
解析:promise的状态只能修改一次,不可多次修改
限制并发
const uris = [
'https://www.kkkk1000.com/images/getImgData/getImgDatadata.jpg',
'https://www.kkkk1000.com/images/getImgData/gray.gif',
'https://www.kkkk1000.com/images/getImgData/Particle.gif',
'https://www.kkkk1000.com/images/getImgData/arithmetic.png',
'https://www.kkkk1000.com/images/getImgData/arithmetic2.gif',
'https://www.kkkk1000.com/images/getImgData/getImgDataError.jpg',
'https://www.kkkk1000.com/images/getImgData/arithmetic.gif',
'https://www.kkkk1000.com/images/wxQrCode2.png',
];
const loadImg = (src) => {
return new Promise((resolve, reject) => {
const img = new Image();
img.onload = () => {
resolve();
};
img.onError = reject;
img.src = src;
});
}
function limitLoad(urls, limit) { };
limitLoad(uris, 2);
答案:
function limitLoad(uris, limit) {
let index = 0;
const len = uris.length;
const execPromised = () => {
if (index < len) {
console.log(index);
loadImg(uris[index]).then(() => {
// setTimeout(() => {
execPromised()
// }, 2000);
});
index++;
}
};
for(let i = 0; i < limit; i++) {
execPromised();
}
}
解析:setTimeout只是用于观察 我们写的 并发是否正确
依次输出结果
function promise (time) {
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve(time);
}, time);
});
}
order([
promise(1000),
promise(5000),
promise(3000),
])
答案1:
function order(promises) {
const len = promises.length;
const res = [];
let promise = promises[0];
for(let i = 1; i <= len; i++) {
promise = promise.then(data => {
if (data) {
res.push(data);
}
return promises[i];
})
}
return res;
}
根据promiseA+实现一个自己的Promise
步骤一:实现成功和失败的回调方法
要实现promise最羁绊的功能,首先,需要创建一个构造函数promise,创建一个promise类,在使用的时候传入一个执行器executor,executor会传入两个参数:成功(resolve)和失败(reject)。前面说过状态只会改变一次,只可能是成功或者失败,所以,默认情况下,在调用成功是,就反悔成功态,调用失败是,反悔失败态。代码如下:
class Promise {
constructor(executor) {
this.status = 'pending';
this.value = undefined;
this.reason = undefined;
this.onResolvedCallbacks = [];
this.onRejectedCallBacks = [];
let resolve = (data) => {
if(this.status !== 'pending') {
return;
}
this.status = 'resolved';
this.value = data;
this.onResolveCallbacks.forEach(fn => fn());
}
let reject = (reason) => {
if(this.status !== 'pending') {
return;
}
this.status = 'rejected';
this.reason = reason;
this.onRejectedCallbacks.forEach(fn => fn());
}
try{
executor(resolve, reject);
} cathc(e) {
reject(e);
}
}
}
步骤二:then方法链式调用
then方法是promise的最基本的方法,入参是两个回调,一个是成功的回调,一个是失败的回调,实现过程如下。
then(onFulfilled, onRejected) {
if(this.status === 'resolved') {
onFulfilled(this.value);
}
if(this.status === 'rejected') {
onRejected(this.reason);
}
}
let p = new Promise(function(resolve) {
resolve('我是成功');
});
p.then((data) => { console.log(data); }, err => {});
p.then((data) => { console.log(data); }, err => {});
为了实现这样的效果,则then的代码将要重新写,我们可以把每次的调用resolve的结果存入到一个数组中,每次调用的reject结果存入一个数组。这就是为何会在上面两个数组,切分别在resolve 和 reject便利两个数组的额原因,因此,在调用resolve或者reject之前,我们的pending状态是,会把多次then中的结果存入数组中,则上面的代码变为,
then(onFulFilled, onRjected) {
if(this.status === 'resolved') {
onFulFilled(this.value);
}
if(this.status === 'rejected') {
onRjected(this.reason);
}
if(this.status === 'pending') {
this.onResolveCallbacks.push(() => {
onFulFilled(this.value);
});
this.onRejectedCallbacks.push(() => {
onRejected(this.reason);
})
}
}
在promise中,要实现链式调用反悔的结果是返回一个新的promise,第一次then中返回的结果,无论是成功或失败,都激昂反悔倒下一次then中的成功态,但是第一次then中如果抛出一次昂错误,则将反悔到下一次then中的失败态中
链式调用成功时
链式调用成功会返回值,有多种情况。因此将链式调用返回的值单独写一个方法。方法中传入四个参数,分别是p2,x,resolve,reject。
- p2 上一次返回的promise对象
- x表示运行promise返回的结果
- resolve是p2的方法
- reject是p2的方法
function resolvePromise(p2, x, resolve, reject) {
……
}
- 返回结果不能是自己,返回结果是自己时,永远也不会成功活失败,因此当反悔自己是,抛出一个错误。
function resolvePromise(p2, x, reslove, reject) {
if(p2 === x) {
return reject(new TypeError('不能引用自己'))
}
}
- 返回结果可能是promise
function resolvePromise(promise2, x, resolve, reject) {
if (promise2 === x) {
return reject(new TypeError('不能引用自己'));
}
else if(x !== null && (typeof x === 'function' || typeof x === 'object')) {
let called; // 防止成功后调用失败
try {
let then = x.then; // 获取x的then方法
if(typeof then === 'function') {
// 就让then执行 第一个参数是this 后面是成功的回调 和 失败的回调
then.call(x, y => {
// 成功和失败只能调用一个
if (called) return;
called = true;
// resolve的结果依旧是promise 那就继续解析
resolvePromise(promise2, y, resolve, reject);
},
err => {
// 成功和失败只能调用一个
if (called) return;
called = true;
reject(err);// 失败了就失败了
})
}
}
catch(e) {
if(called) return;
called = true;
reject(e);
}
}
else {
resolve(x);
}
}