promise是一个构造函数,promise对象用来封装一个异步操作并可以获取其成功或失败的结果值
为什么要用promise?
- promise指定回调函数的方式更加灵活 旧的:必须在异步任务前指定 promise:启动异步任务→返回promise对象→给promise对象绑定回调函数
- 支持链式调用,可以解决回调地域的问题 回调地狱:回调函数嵌套调用,外部回调函数异步执行的结果是嵌套的回调函数执行的条件
then catch
在then和catch中,如果正常,返回resolve,报错返回reject
例子
const myPromise = new Promise((resolve, reject) => {
const success = true;
if (success) {
resolve("Operation was successful!");
} else {
reject("Operation failed.");
}
});
**myPromise
.then((result) => {
console.log(result); // 如果操作成功,这里会输出 "Operation was successful!"
})
.catch((error) => {
console.error(error); // 如果操作失败,这里会输出 "Operation failed."
});**
Promise的状态
- Pending:待定,初始状态,既没有被解决也没有被拒绝
- Fulfilled:已完成,操作成功完成,并返回了结果
- Rejected:已拒绝,操作失,并返回了一个原因
Promise的常用方法
Promise.resolve(value)
- 如果传入的参数为非promise类型的对象,则返回结果为成功的promise对象。
- 如果传入的参数为promise对象,则参数是成功的就返回成功的,失败的就返回失败的
let p1 = Promise.resolve(666) //这个就是 非promise对象
let p2 = Promise.resolve(new Promise((resolve) =>{
resolve('ok')
}
Promise.reject()
返回一个失败的promise对象
Promise.all()
参数为n个promise对象,当n个promise对象都成功的时候返回一个成功的promise对象,只要有一个失败了就返回失败的promise对象
promise.race()
参数为n个promise对象,第一个完成的promise对象是成功的就返回成功,失败则失败
Promise的习题(事件循环)
1
const promise = new Promise((resolve, reject) => {
console.log(1);
resolve('success')
console.log(2);
});
promise.then(() => {
console.log(3);
});
console.log(4);
过程:
- 先遇到new Promise 执行其中的打印1
- 遇到resolve,将promise的状态改为resolve
- 打印2
- 遇到promise.then这个微任务,将其加入微任务队列,继续向下执行宏任务
- 打印4
- 宏任务完毕,执行微任务队列,promise此时状态为resolve所以执行then,打印3
结果:1243
2
const fn = () =>
new Promise((resolve, reject) => {
console.log(1);
resolve("success");
});
console.log("start");
fn().then(res => {
console.log(res);
});
注意,fn是一个函数的形式,上面只是定义了fn为一个promise,但他没有执行fn().then 的含义为先执行fn这个函数 再定一的then方法,因此执行过程为
'start'
1
'success'
3
const promise = new Promise((resolve, reject) => {
console.log(1);
setTimeout(() => {
console.log("timerStart");
resolve("success");
console.log("timerEnd");
}, 0);
console.log(2);
});
promise.then((res) => {
console.log(res);
});
console.log(4);
过程:
- 打印1
- 遇到了stetimeout 加入宏任务队列
- 打印2
- 遇到promise.then,但是现在不知道promise的状态,因此先跳过
- 打印4
- 执行第二个宏任务settimeout,打印timerstart
- 将promise的状态设定为成功
- 打印timerend
- 执行then方法,打印success
答案:
1
2
4
"timerStart"
"timerEnd"
"success"
4
Promise.resolve(1)
.then((res) => {
console.log(res)
return 2
})
.catch((err) => {
return 3
})
.then((res) => {
console.log(res)
})
- 调用promise.resolve传参为1
- 执行then 打印1
- return 2 的意思是链式调用,继续传递一个Promise.resolve(2)
- 执行 打印2
1
2
5
Promise.resolve()
.then(() => {
console.log('then')
})
process.nextTick(() => {
console.log('nextTick')
})
setImmediate(() => {
console.log('setImmediate')
})
console.log('end')
- 执行第一个宏任务,打印end
- 执行微任务队列 nextTick优先级高于promise.then(规定) 打印nextTick
- 打印then
- 执行下一个宏任务setImmediate
end
nextTick
then
setImmediate
手撕promise.all
Promise.all是返回一个新的Promise对象,如果传入参数里的所有Promise对象都是成功则返回成功,失败则返回失败。
function myPromiseAll(promises) {
return new Promise((resolve, reject) => {
if(!Array.isArray(promises)){
return reject(new TypeError('输入参数需要为数组'))
}
//储存Promise结果的数组 和 计数
const ans = []
let count = 0
//如果传入参数是一个空数组,返回一个空的promise正确对象
if(promises.length === 0) return resolve(ans)
//遍历每一个promises对象
promises.forEach((item, index)=> {
Promise.resolve(item)
.then(result => {
ans[index] = result
count++
if(count === promises.length) resolve(ans)
})
.catch(error=>{
reject(error)
})
})
})
}
手撕Promise.race
接收一堆Promise 最先返回的Promise对象是成功就返回成功的Promise对象,失败则返回失败的Promise对象.如果传入的参数不是数组,则报错。传入的数组长度为0 直接return
function myPromiseRace(promises) {
return new Promise((resolve, reject) => {
if (!Array.isArray(promises)) {
return reject(new TypeError('输入参数需要为数组'))
}
if (promises.length === 0) return
promises.forEach(item => {
Promise.resolve(item)
.then(resolve)
.catch(reject)
})
})
}
如何用Promise实现按顺序调用三个接口,以及后续可能的问题
可以利用promise的链式调用,用then,每个返回后都return下一个对应的promise
function api1() {
return new Promise((resolve) => setTimeout(() => resolve("API1 resolved"), 1000));
}
function api2() {
return new Promise((resolve) => setTimeout(() => resolve("API2 resolved"), 1000));
}
function api3() {
return new Promise((resolve) => setTimeout(() => resolve("API3 resolved"), 1000));
}
// 按顺序调用
api1()
.then((res1) => {
console.log(res1); // API1 resolved
return api2();
})
.then((res2) => {
console.log(res2); // API2 resolved
return api3();
})
.then((res3) => {
console.log(res3); // API3 resolved
})
.catch((err) => {
console.error("Error occurred:", err);
});
多个接口同时调用怎么办?
在封装完3个api函数后,使用Promise.all进行
Promise.all([api1(), api2(), api3()])
.then((results) => {
console.log("All results:", results);
})
.catch((err) => {
console.error("Error in one of the promises:", err);
});
如何处理某个逻辑的超时逻辑?
封装一个超时函数
function withTimeout(promise, ms) {
return Promise.race([
promise,
new Promise((_, reject) =>
setTimeout(() => reject(new Error("Timeout")), ms)
),
]);
}
withTimeout(api1(), 2000)
.then((res) => console.log(res))
.catch((err) => console.error(err));
题目
Promise.resolve('A')
.then('B')
.then(Promise.resolve('C'))
.then(console.log);
输出 A
then catch的参数都必须为函数 否则会被忽略
题目
Promise.resolve().then(() => {
return new Error('error')
}).then(res => {
console.log("then: ", res)
}).catch(err => {
console.log("catch: ", err)
})
输出:then: Error: error
new Error不会改变Promise的状态
题目
const newPromise1 = new Promise((resolve, reject) => {
console.log('A');
resolve('B');
});
const newPromise2 = newPromise1.then(res => {
console.log(res);
});
console.log('C', newPromise1);
console.log('D', newPromise2);
先打印A,然后执行resolve,接着遇到newpromise2, 由于里面的.then属于微任务,因此稍后执行,打印C,并且为fufilled状态,打印D,由于未执行then,因此未pending状态
A
C fulfilled
D Pending
B
程序输出题目
Promise.resolve().then(() => {
console.log('outerPromise');
const innerTimer = setTimeout(() => {
console.log('innerTimer')
}, 0)
});
const timer1 = setTimeout(() => {
console.log('outerTimer')
Promise.resolve().then(() => {
console.log('innerPromise')
})
}, 0)
console.log('run');
要记住settimeout属于宏任务
run
outerPromise
outerTimer
innerPromise
innerTimer
程序输出题
Promise.resolve('A')
.then('B')
.then(Promise.resolve('C'))
.then(console.log)
如果then接受到的是一个非函数的值,比如字符串或者一个被解析了的Promise对象,那么会忽略他
A
promise1 pending promise2 pending
innerpromise1 fulfilled
innerPromise2 reject
程序输出题
Promise.resolve('A')
.then(res => {
console.log(res);
return 'B';
})
.catch(err => {
return 'C';
})
.then(res => {
console.log(res);
});
链式调用中,return的话会把值进行传递
A
B
程序输出题
Promise.reject('error')
.then((res) => {
console.log('succeed', res)
}, (err) => {
console.log('innerError', err)
}).catch(err => {
console.log('catch', err)
})
在then中,接受了两个参数,一个是成功,一个是失败。当接受失败的实行了,then往下传递的是一个成功的Promise,因此catch不会被执行
innerError error
程序输出题
Promise.resolve('A')
.then(res => {
console.log('promise1', res)
})
.finally(() => {
console.log('finally1')
})
Promise.resolve('B')
.finally(() => {
console.log('finally2')
return 'result'
})
.then(res => {
console.log('promise2', res)
})
先执行同步代码,
创建Promise,并resolve(A),遇到then,将其加入微任务队列,
继续执行同步代码,Promise.resolve(B)
同步代码执行完,执行微任务,输出 promise1 A
遇到finally1,加入微任务队列
按次序执行微任务,输出finally2 ,遇到.then 继续加入微任务队列
按次序,输出finally1 再输出promise2
另外,finally,不会改变链式调用中后续的promise的值
promise1 A
finally2
finally1
promise2 B
promiseAll的程序输出
function runAsync(num) {
return new Promise(
r => setTimeout(
() => r(num, console.log(num)
), 1000)
)
}
Promise.all([runAsync(1), runAsync(2), runAsync(3)])
.then(res => console.log(res));
promiseAl的程序输出
function runAsync(num) {
return new Promise((resolve) => setTimeout(
() => resolve(num, console.log(num)), 1000)
);
}
function runReject(num) {
return new Promise((resolve, reject) => setTimeout(
() => reject(`Error: ${num}`, console.log(num)), 1000 * num)
);
}
Promise.all([runAsync(1), runReject(4), runAsync(3), runReject(2)])
.then((res) => console.log(res))
.catch((err) => console.log(err));
console.log 由于是一个表达式,因此会直接输出出来,但也不影响它作为一个类似变量的东西进行传递。
promiseAll,如果遇到reject的会返回出第一个遇到的错误的promise。成功的话会返回所有成功的
1
3
2
Error: 2
4