前端开发中有一些 Api 需要通过回调处理一些异步操作,但是我们更愿意使用 Promise的调用方式。那么什么时候我们使用回调函数什么时候使用Promise呢?
将callback函数转换为Promise
假设我需要计算文件sha256值,由于读取文件是异步操作,我需要在onloadend的回调结果中处理reader.result
uploadFile(file){
let reader = new FileReader()
reader.readAsBinaryString(file)
reader.onloadend = () = >{
let hashCode = this.SHA256.convertToSHA256(reader.result)
// todo
}
}
使用async函数,await转换
async uploadFile(file) {
let fileBlob = await new Promise((resolve,reject)=>{
let reader = new FileReader()
reader.readAsBinaryString(file)
reader.onloadend = () = >{
resolve(reader.result)
}
})
this.SHA256.convertToSHA256(fileBlob)
// todo
}
自定义Sleep函数
function Sleep(micsec) {
return new Promise((resolve) => {
setTimeout(resolve, micsec);
});
}
使用Promise避免多层嵌套的回调
// 处理前
() => {
A.callback = (B) => {
B.callback = (C) => {
C.callback = (D) => {
// .....
};
};
};
};
// 处理后
async () => {
let B = await new Promise((resolve, reject) => {
A.callback = resolve;
});
let C = await new Promise((resolve, reject) => {
B.callback = resolve;
});
// ...
};
callback一定可以转化成Promise写法?
答案是 no!回调函数和 Promise 本质上不是一回事,能够相互转化的场景是因为两者都能处理异步场景。
回调函数和 Promise 的区别至少在于:
-
callback可以多次被回调并传入不同值
-
promise拥有不可逆状态
从 pending->fulfilled, pending->rejected 的状态是不可逆的。譬如:
new Promise((res) => {
console.log("only printed once.");
res("finish.");
})
.then((res) => {
console.log(1, res);
})
.then((res) => {
console.log(2, res);
});
// 依次打印的值是:
// only printed once.
// 1 finish.
// finish.
可知,单个Promise状态是不能改变的(链式调用过程中可以改变 promise 状态),并且 value 或者 error 会被缓存下来。这个特性在需要设计中可以被利用。
而callback是可以被多次执行的,其参数相当于传递给下一层的 value 或 error 却是可以被多次改变的。例如:
function loadData(callback) {
let finishLoad = false;
// 从缓存加载数据,一般比网络加载要快
loadAtStorage().then((data) => {
if (finishLoad || !data) {
return;
}
callback(data);
});
// 从网络加载
loadAtNetwork().then((data) => {
finishLoad = true;
callback(data);
});
}
按正常情况看,加载数据函数中callback函数会先后被回调两次。第一次先使用本地数据同时等待网络数据加载完毕,更新网络数据。