Promise
在事件循环中提到了promise
,async/await
中提到了promise
,于是来深入学习一下Promise
。
什么是Promise
promise
是JS中进行异步编程的新的解决方案。在promise
中,有三种状态pending
、resolved
和rejected
。状态只能从pending
变为resolved
或者rejected
,状态一旦发生改变,就不能再变了。不管是成功还是失败,最后都会返回一个Promise
对象。
为什么使用Promise
-
promise
的回调机制更灵活,你可以在异步任务之后再指定回调函数:// 使用纯回调函数的时候,需要再开启任务的时候同时指定成功和失败回调 todoSomeThing(getList, successCallback, failCallBack); // 如果使用Promise的话 const p = todoSomething(getList); p.then(successCallback, failCallBack);
-
支持链式调用,可以解决回调地狱的问题;
// 纯回调函数 todoSomeThing(function(res){ todoSomeThingElse(res, function(newRes){ todoOtherThing(newRes, function(finalResult){ console.log('Got the final result:', finalResult); }, failCallback); }, failCallback) }, failCallback); // 使用Promise的话可以使用链式调用解决这个问题 todoSomeThing.then(res => { return todoSomeThingElse(res); }).then(res => { return todoOtherThing(res); }).then(res => { console.log('Got the final result:', res); }).catch(failCallback);
-
如果觉得链式调用也不好用的话,可以结合
async/await
来解决:async function request(){ try{ const res1 = await todoSomeThing(); const res2 = await todoSomeThingElse(res1); const res3 = await todoOtherThing(res2); console.log('Got the final result:', res3); } catch(err => { failCallback(err); }) }
Promise的API
// (resolve, reject) => {} 是执行器, 它会在会在 Promise 内部立即同步回调。
new Promise((resolve, reject) => {});
// Promise.prototype.then里面接受两个参数,一个是为resolve时的成功回调,一个是reject时的错误回调,一般会省略不写,然后由catch接受
new Promise((resolve, reject) => {
// ...
}).then(res => {}, err => {});
// Promise.prototype.catch里面接受一个错误回调函数,它是then的语法糖,相当于then(undefined, err => {});如果then里面有错误回调,且错误回调没有抛出异常,或者没有使用Promise.reject的话,不会走到catch
new Promise((resolve, reject) => {
// ...
}).then(res => {
// ...
}).catch(err => {});
new Promise.resolve();
// 返回一个失败的Promise对象
new Promise.reject();
// 传入多个Promise对象,返回一个新的Promise, 只有所有的Promise都成功才算成功,只要有一个失败了就 直接失败
new Promise.all([
new Promise(res => {}, err => {}),
new Promise(res => {}, err => {})
]);
// 传入多个Promise对象,返回一个新的Promise, 第一个完成的 promise 的结果状态就是最终的结果状态
new Promise.race([
new Promise(res => {}, err => {}),
new Promise(res => {}, err => {})
]);
// 传入多个Promise对象,返回一个新的Promise, 可以获得每个Promise的状态
const promise1 = Promise.resolve(3);
const promise2 = new Promise((resolve, reject) => setTimeout(reject, 100, 'foo'));
const promises = [promise1, promise2];
Promise.allSettled(promises).
then((results) => results.forEach((result) => console.log(result.status)));
// 打印结果: "fulfilled" "rejected"
// 传入多个Promise对象,返回一个新的Promise, 当有一个promise成功就返回成功(resolve)状态,或者所有的 promises 都失败,那么返回的 promise 就会 异步地(当调用栈为空时) 变成成功/失败(resolved/reject)状态。
new Promise.any([
new Promise(res => {}, err => {}),
new Promise(res => {}, err => {})
]);
Promise常见问题
-
改变
Promise
状态的方式:Promise.resolve、Promise.reject和 throw new Error
; -
一个 promise 指定多个成功/失败回调函数时,当 promise 改变为对应状态时都会调用;
-
promise.then
返回的新Promise
的结果有那些:-
如果抛出异常,新的
promise
变为reject
; -
如果返回的是非
promise
的任意值,新promise
会变为resolve
; -
如果返回的另一个
promise
,这个promise
会变为新的promise
的结果。// 1. 在then中抛出异常,新的promise会变为reject new Promise((resolve, reject) => { resolve(1); }).then(res => { throw new Error(2); }).catch(err => { console.log(err); }) // 2. 如果返回的是非promise的任意值,新promise会变为resolve new Promise((resolve, reject) => { resolve(1); }).then(res => { return 2; }).then(res => { console.log(res); // 2 }) // 3. 如果返回的另一个promise,这个promise会变为新的promise的结果 new Promise((resolve, reject) => { resolve(1); }).then(res => { return Promise.resolve(2); }).then(res => { console.log(res); // 2 })
-
-
中断
promise
:new Promise((resolve, reject) => { return new Promise(() => {}); // 返回一个pendding状态的promise对象 })
手写Promise
创建文件:
- index.js
- promise.js
第一步:
正常情况下我们是这么调用promise
的:
// index.js
let t = new Promise((resolve, reject) => {
resolve('Hello World');
})
t.then((res) => {
console.log(res);
}, (err) => {
console.log(err);
})
// 输入 Hello Wolrd
由此我们可以看出,Promise
是一个构造函数或者类,且含有一个then
方法。
// promise.js
// 先定义三个状态常量
const PENDING = 'pending';
const RESOLVED = 'resolved'; // 也可以标准些,写成fulfilled
const REJECTED = 'rejected';
class myPromise {
status = PENDING; // 记录当前的状态
result = undefined; // 状态为resolved时返回的结果
reason = undefined; // 状态为rejected时返回的结果
constructor(excutor){
const resolve = (result) => {
this.status = RESOLVED;
this.result = result;
}
const reject = (reason) => {
this.status = REJECTED;
this.reason = reason;
}
excutor(resolve, reject); // new Promise时是立即执行的,且需要两个参数
}
then(onResolved, onRejected){
if(this.status === RESOLVED){ // 如果当前状态是`resolved`则调用onResolved
onResolved(this.result);
}
if(this.status === REJECTED){ // / 如果当前状态是`rejected`则调用onRejected
onRejected(this.reason);
}
}
}
module.exports = myPromise;
在index.js
中使用:
const MyPromise = require('./promise');
const t = new MyPromise((resolve, reject) => {
resolve('Hello wolrd');
})
t.then((res => {
console.log(res);
}))
// 输入 Hello Wolrd
第二步:
// 使用自定义的Promise
// 这时候是先输出Hello World 、 123
const MyPromise = require('./promise');
const t = new MyPromise((resolve, reject) => {
resolve('Hello wolrd');
})
t.then((res => {
console.log(res);
}))
console.log('123')
// 使用原生的Promise
// 这时候是先输出 123 Hello World
const t = new Promise((resolve, reject) => {
resolve('Hello wolrd');
})
t.then((res => {
console.log(res);
}))
console.log('123')
所以我们应该让then
执行晚一些:
// myPromise 修改then中的方法,加入setTimeout
then(onResolved, onRejected){
if(this.status === RESOLVED){
setTimeout(() => {
onResolved(this.result);
})
}
if(this.status === REJECTED){
setTimeout(() => {
onRejected(this.reason);
})
}
}
第三步:
// 使用原生的Promise
// 输出 Hello World
const t = new Promise((resolve, reject) => {
setTimout(() => {
resolve('Hello wolrd');
});
})
t.then((res => {
console.log(res);
}))
但是使用我们自定的myPromise
并不会有任何输出,因为在调用then
时执行了内部的setTimeout
,但是会延迟执行,导致此时的statue = PENDING
,所以在then
中没有任何处理,所以我们要将在PENDING
时将then
的回调函数存起来,在改变状态的时候调用。
class MyPromise {
status = PENDING;
result = undefined;
reason = undefined;
onResolvedArr = []; // 第一步:声明存放then中的resolve的变量
onRejectArr = []; // 声明存放then中的reject的变量
constructor(excutor){
const resolve = (result) => {
if(this.status === PENDING){
this.status = RESOLVED;
this.result = result;
this.onResolvedArr.map((fn) => fn()); // 第三步,在改变状态的同时,看看有无回调函数并调用。
}
};
const reject = (reason) => {
if(this.status === PENDING){
this.status = REJECTED;
this.reason = reason;
this.onRejectArr.map((fn) => fn()); // 第三步,在改变状态的同时,看看有无回调函数并调用。
}
};
excutor(resolve, reject);
}
then(onResolved, onRejected){
if(this.status === RESOLVED){
setTimeout(() => {
onResolved(this.result);
})
}
if(this.status === REJECTED){
setTimeout(() => {
onRejected(this.reason);
})
}
if(this.status === PENDING){ // 第二步: 在pending时,将这些回调函数存放起来
this.onResolvedArr.push(() => {
onResolved(this.result);
});
this.onRejectArr.push(() => {
onRejected(this.reason);
})
}
}
}
第四步:
// 原生Promise中的then是支持链式调用的
const t = new Promise((resolve, reject) => {
setTimeout(() => {
resolve('Hello wolrd');
})
})
t.then((res => {
return res;
})).then((res) => {
console.log(res); // Hello Wolrd
})
我们需要给我们的自定义Promise
中的then
也支持链式调用,所以内部应该返回一个new MyPromise
:
then(onResolved, onRejected){
const newPromise = new MyPromise((resolve, reject) => {
if(this.status === RESOLVED){
setTimeout(() => {
const result = onResolved(this.result);
resolve(result);
})
}
if(this.status === REJECTED){
setTimeout(() => {
const result = onRejected(this.reason);
reject(result);
})
}
if(this.status === PENDING){
this.onResolvedArr.push(() => {
const result = onResolved(this.result);
resolve(result);
});
this.onRejectArr.push(() => {
const result = onRejected(this.reason);
reject(result);
})
}
})
return newPromise;
}
第五步:
以上代码只满足链式调用中then
是直接返回一个值的情况,如果是下面这种情况就不行了:
const t = new Promise((resolve, reject) => {
setTimeout(() => {
resolve('Hello wolrd');
})
})
t.then((res => {
return new Promise((resolve, reject) => {
resolve('test');
});
})).then((res) => {
console.log(res);
})
修改then
,使用handlePromise
来处理:
then(onResolved, onRejected){
const newPromise = new MyPromise((resolve, reject) => {
if(this.status === RESOLVED){
setTimeout(() => {
const result = onResolved(this.result);
handlePromise(result, newPromise, resolve, reject);
})
}
if(this.status === REJECTED){
setTimeout(() => {
const result = onRejected(this.reason);
handlePromise(result, newPromise, resolve, reject);
})
}
if(this.status === PENDING){
this.onResolvedArr.push(() => {
const result = onResolved(this.result);
handlePromise(result, newPromise, resolve, reject);
});
this.onRejectArr.push(() => {
const result = onRejected(this.reason);
handlePromise(result, newPromise, resolve, reject);
})
}
})
return newPromise;
}
const handlePromise = (result, newPromise, resolve, reject) => {
// result与newPromise不能相等,不然循环引用时会出现问题
if(result === newPromise){
return reject(new TypeError('error'));
}
if((typeof result === 'object' && result !== null) || typeof result === 'function'){
const then = result.then;
if(typeof then === 'function'){
// 如果then是一个函数,则递归调用handlePromise使得最后的值不是函数
then.call(result, r => {
handlePromise(r, newPromise, resolve, reject);
}, e => {
reject(e);
})
}else {
resolve(result);
}
}else {
// 如果是返回一个变量,则直接resolve
resolve(result);
}
}
第六步:
加上一些try catch
确保容错:
// 定义三种状态
const RESOLVED = 'resolved';
const REJECTED = 'rejecte';
const PENDING = 'pending';
const handlePromise = (result, newPromise, resolve, reject) => {
// 如果是自己就抛出异常
if(result === newPromise){
throw new Error('can not return oneself');
}
if((typeof result === 'object' && result !== null) || typeof result === 'function'){
const then = result.then;
if(typeof then === 'function'){
try {
then.call(result, r => {
handlePromise(r, newPromise, resolve, reject);
}, e => {
reject(e);
})
} catch (error) {
reject(error);
}
}else {
resolve(result);
}
}else {
resolve(result);
}
}
class MyPromise {
status = PENDING;
result = undefined;
reason = undefined;
onResolvedArr = [];
onRejectArr = [];
constructor(excutor){
const resolve = (result) => {
if(this.status === PENDING){
this.status = RESOLVED;
this.result = result;
this.onResolvedArr.map((fn) => fn());
}
};
const reject = (reason) => {
if(this.status === PENDING){
this.status = REJECTED;
this.reason = reason;
this.onRejectArr.map((fn) => fn());
}
};
try {
excutor(resolve, reject);
} catch (error) {
reject(error);
}
}
then(onResolved, onRejected){
onResolved = typeof onResolved === 'function' ? onResolved : (data) => data;
onRejected = typeof onRejected === 'function' ? onRejected : (err) => { throw new Error(err) };
const newPromise = new MyPromise((resolve, reject) => {
if(this.status === RESOLVED){
setTimeout(() => {
try {
const result = onResolved(this.result);
handlePromise(result, newPromise, resolve, reject);
} catch (error) {
reject(error);
}
})
}
if(this.status === REJECTED){
setTimeout(() => {
try {
const result = onRejected(this.reason);
handlePromise(result, newPromise, resolve, reject);
} catch (error) {
reject(error);
}
})
}
if(this.status === PENDING){
this.onResolvedArr.push(() => {
try {
const result = onResolved(this.result);
handlePromise(result, newPromise, resolve, reject);
} catch (error) {
reject(error);
}
});
this.onRejectArr.push(() => {
try {
const result = onRejected(this.reason);
handlePromise(result, newPromise, resolve, reject);
} catch (error) {
reject(error);
}
})
}
})
return newPromise;
}
catch(onRejected){
this.then(undefined, onRejected);
}
}
module.exports = MyPromise;
Promise
的基本实现就完成了。
参考
若有错误,欢迎指出,感谢~