昨天面试中,面试官问我promise与async的问题,并询问我喜欢用哪一个。
我回答道,在封装ajax的时候会用到promise,但是在页面中写使用的时候会使用async。
面试官问我为什么,我直接就想到了在页面中使用async的话可以使代码看上去更简洁更易读。
然后他继续追问我那为什么在封装ajax模块的时候不使用async,不是使用async更简洁更易读嘛。
我当时心里咯噔一下,我不禁也在想你真的是个天才。
为什么不用async我们网络上看到的封装ajax都是使用的promise这是为什么呢?
promise
最常用的写法
function resolveCallback(flag){
return new Promise((resolve,reject)=>{
// 处理异步任务
setTimeout(() => {
if (flag) {
resolve('成功')
} else {
reject('失败')
}
}, 1000)
})
}
resolveCallback(true)
.then(res =>{})
.catch(res => {})
promise翻译过来的意思是保证,我们在日常生活中听到的最多的保证,保证我以后不会在犯错了,保证的是未来的一个事情,promise也一样只是跟人不同它是真会保证,它会保证在未来的时间中会给你一个结果,但是它也不知道多久给你,它也不知道这个结果是好的结果还是坏的结果,所以你的需要.then和.catch两种方式去接,分别对应了好结果与坏结果
console.log('我肯定先执行')
const resolveCallback = (): Promise<string> => {
return new Promise((reject, resolve) => {
setTimeout(()=>{
reject('我是Promise')
},1000)
})
}
resolveCallback().then(res=>{
console.log(res)
})
console.log('我在外面')
上面代码打印出来的顺序是什么呢?
答案是
我肯定先执行
我在外面
我是Promise
async
最常用的写法
async function test(){
return 'ssss'
}
//等价于
function test(){
return new Promise((resolve,reject)=>{
resolve('ssss')
})
}
//都可以使用来接受这玩意
test().then(res=>{
})
其实到这里的时候我大概就已经有点明白为什么不用async封装ajax了,因为它没有resolve,reject这两种方法,它确实更简洁,但是在对于ajax这种我们希望在调用失败的时候做些什么操作的时候就显得没有那么方便了,反而看起来较为臃肿的promise方法因为有resolve,reject的加成除了起来相当的得心应手。
下面我们看看promise源码,探索底层嗷
class Promise1 {
constructor(fn) {
// resolve时的回调函数列表
this.resolveTask = [];
// reject时的回调函数列表
this.rejectTask = [];
// state记录当前状态,共有pending、fulfilled、rejected 3种状态
this.state = "pending";
//方法
let resolve = value => {
// state状态只能改变一次,resolve和reject只会触发一种
if (this.state !== "pending") return;
//改变状态
this.state = "fulfilled";
this.data = value;
// 模拟异步,保证resolveTask事件先注册成功,要考虑在Promise里面写同步代码的情况
setTimeout(() => {
this.resolveTask.forEach(cb => cb(value));
});
};
let reject = err => {
if (this.state !== "pending") return;
//改变状态
this.state = "rejected";
this.error = err;
// 保证rejectTask事件注册成功
setTimeout(() => {
this.rejectTask.forEach(cb => cb(err));
});
};
// 关键代码,执行fn函数
try {
fn(resolve, reject);
} catch (error) {
reject(error);
}
}
then(resolveCallback, rejectCallback) {
// 解决链式调用的情况,继续返回Promise
return new Promise((resolve, reject) => {
// 将then传入的回调函数,注册到resolveTask中
this.resolveTask.push(() => {
// 重点:判断resolveCallback事件的返回值
// 假如用户注册的resolveCallback事件又返回一个Promise,
//将resolve和reject传进去,这样就实现控制了链式调用的顺序
const res = resolveCallback(this.data);
if (res instanceof Promise) {
res.then(resolve, reject);
} else {
// 假如返回值为普通值,resolve传递出去
resolve(res);
}
});
this.rejectTask.push(() => {
// 同理:判断rejectCallback事件的返回值
// 假如返回值为普通值,reject传递出去
const res = rejectCallback(this.error);
if (res instanceof Promise) {
res.then(resolve, reject);
} else {
reject(res);
}
});
});
}
}
知识点补充:
上面的代码有一个不带时间的setTimeout其起到的作用是让事件先注册成功再执行setTimeout中的代码。如下代码
setTimeout(() => {
console.log(222)
});
for (let index = 0; index < 1000000000; index++) {
if(index===999999999){
console.log(111)
}
}
/*
打印内容如下:
111
222
*/