JS 异步解决方案的发展历程以及优缺点
程序员文章站
2022-04-15 12:40:15
...
- 回调函数(callback)
setTimeout(() => {
// callback 函数体
}, 1000)
缺点:回调地狱,不能用 try catch 捕获错误,不能 return
回调地狱的根本问题在于:
缺乏顺序性: 回调地狱导致的调试困难,和大脑的思维方式不符
嵌套函数存在耦合性,一旦有所改动,就会牵一发而动全身,即(控制反转)
嵌套函数过多的多话,很难处理错误
ajax('XXX1', () => {
// callback 函数体
ajax('XXX2', () => {
// callback 函数体
ajax('XXX3', () => {
// callback 函数体
})
})
})
优点:解决了同步的问题(只要有一个任务耗时很长,后面的任务都必须排队等着,会拖延整个程序的执行。)
2. Promise
Promise就是为了解决callback的问题而产生的。
Promise 实现了链式调用,也就是说每次 then 后返回的都是一个全新 Promise,如果我们在 then 中 return ,return 的结果会被 Promise.resolve() 包装
优点:解决了回调地狱的问题
function promise(a) {
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve(a)
}, 1000)
})
}
var arr = [];
new Promise((resolve, reject) => {
setTimeout(() => {
resolve(1)
}, 1000)
}).then(val => {
arr.push(val);
//要执行下一步promise,必须return出来,后面的then才能收到
return promise(2)
}).then(val => {
arr.push(val);
return promise(3)
}).then(val => {
arr.push(val);
console.log(arr)//返回[1,2,3]
})
/*
promise.all
(三个异步方法可同步执行, 但是必须等最慢的方法执行完成后才可执行then)
如果三个方法不相互依赖(没有调用顺序, 也不需要调用异步方法的返回值)可以使用
*/
function fun(a,time) {
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve(a)
}, time)
})
}
Promise.all([fun(1,1000),fun(2,2000),fun(3,3000)]).then(res=>{
console.log(res)//返回[1,2,3]
})
缺点:无法取消 Promise ,错误需要通过回调函数来捕获, 如果连续调用ajax,则需在错误方法throw err
3. Generator
特点:可以控制函数的执行,它不同于普通函数,是可以暂停执行的,所以函数名之前要加星号,以示区别。其实整个 Generator 函数就是一个封装的异步任务,或者说是异步任务的容器。异步操作需要暂停的地方,都用 yield 语句注明。
function* fetch() {
yield ajax('XXX1', () => {})
yield ajax('XXX2', () => {})
yield ajax('XXX3', () => {})
}
let it = fetch()
let result1 = it.next()//执行第一个yield,next也可以获取函数的返回值, next是马上执行, 如果是异步方法则返回值捕获不到
let result2 = it.next()//执行第二个yield
let result3 = it.next()//执行第三个yield
- Async/await
async、await 是异步的终极解决方案
优点是:代码清晰,不用像 Promise 写一大堆 then 链,处理了回调地狱的问题
缺点:await 将异步代码改造成同步代码,如果多个异步操作没有依赖性而使用 await 会导致性能上的降低。
var arr = [];
//awit async
function fun(a) {
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve(a)
}, 1000)
})
}
async function load(){
//代码会等待前一步执行完后再执行下一步
// 如果这三个方法没有依赖性的话,完全可以使用 Promise.all 的方式
// 如果有依赖性的话,其实就是解决回调地狱的例子了
arr.push(await fun(1));
arr.push(await fun(2));
arr.push(await fun(3));
console.log(arr)
}
load()
const asynchronous = time => {
return new Promise((resolve, reject) => {
time == 5000 && reject('时间过长')
setTimeout(() => {
resolve(`${time}ms的异步执行完成`)
}, time);
})
}
/*
在async函数中, 只要遇到await关键字的函数(这个函数需返回promise对象),
函数的运行就会被阻塞, 必须等待第一个await执行完成,才会执行下面的语法块,
其中await的resolve结果会直接返回出来,但reject的结果会当做异常处理
并且async函数中可直接用try catch来捕获await中的异步异常
*/
const fun = async () => {
try {
let res1 = await asynchronous(2000);
let res2 = await asynchronous(2000);
let res3 = await asynchronous(5000);
console.log(res1, res2, res3)
} catch (error) {
console.log(error)
}
}
fun()
推荐阅读