欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

JS 异步解决方案的发展历程以及优缺点

程序员文章站 2022-04-15 12:40:15
...
  1. 回调函数(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
  1. 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()