这四种JS异步编程方法你知道吗?

前言

首先,异步(async)编程是相对于同步(sync)编程而言,所谓同步编程就是按照代码的执行顺序,一个进程执行完成以后再调用下一个进程的过程,由于JavaScript是单线程的语言,在JS中大部分代码都是同步执行的,但是这会导致一个问问题:如果在调用的过程中,执行了I/O操作或者一些耗时的进程,进程就会阻塞,后面的进程也只能等待前面的进程执行完成以后再执行.因此有了异步编程的出现.下面介绍JS中的四种异步编程模式.

一.回调函数(callback)

回调函数(callback)是在主函数的参数中传入一个函数,这个传入的函数就是回调函数,如下:

function say(call_fun) {
    
    
    console.log(111);//111
    call_fun();//这是一个回调函数
}

say(function(){
    
    
    console.log('这是一个回调函数');
})

那么,回调函数是如何处理异步程序的呢?
回调函数的使用;
可以通过不断的在函数中使用回调函数来实现异步,注意:JS中setTimeout是异步任务.如下:

setTimeout(() => {
    
    
    //异步操作
    setTimeout(() => {
    
    
        // 异步操作
        setTimeout(() => {
    
    
            //异步操作
            setTimeout(() => {
    
    
                //异步操作
            }, 2000);
        }, 2000);
    }, 2000);
}, 2000);

通过以上代码可以发现回调函数实现的异步编程有一个致命的缺点,那就是产生回调地狱(callback hell),这不仅影响代码的可读性,对后期维护和排错也会产生很多不必要的麻烦.

二.promise

promise最早是社区提出来的解决方案,最后被纳入官方标准,promise解决的就是回调地狱的问题.
首先来看一下promise的使用:
通过创建一个Promise对象,在对象中传入一个回调函数,函数包括两个参数resolvereject,在在异步操作成功以后调用resolve函数,在异步操作失败以后调用reject函数.这两个函数都可以继续传入参数,在then()方法中接收参数.

let prom = new Promise(function(resolve,reject){
    
    
    resolve('success');//在异步操作成功以后调用
    reject('faild');//在异步操作失败以后调用
})
prom.then(function(data){
    
    
    console.log(data);//resolve调用后进入的函数
},function(data){
    
    
    console.log(data);//reject调用后进入的函数
})

还有一种写法:

let prom = new Promise(function(resolve,reject){
    
    
    resolve('success');//在异步操作成功以后调用
    reject('faild');//在异步操作失败以后调用
})
prom.then(function(data){
    
    
    console.log(data);//success   resolve调用后进入的函数 
}).catch(function(data){
    
    
    console.log(data);// faild   reject调用后进入的函数
})

当在实际应用中需要不断链式调用的时候,我们需要在then的方法中返回一个新的promise对象,这样就可以不断链式调用。上面的例子用promise写法如下:
注意:在promise里面 异步任务以外的操作仍然是同步任务.

let prom = new Promise(function (resolve, reject) {
    
    
    setTimeout(() => {
    
    
        //异步操作
        console.log(111);
        resolve('success');
    }, 2000);

})
prom.then(function (data) {
    
    
    console.log(data);//success
    return new Promise(function (resolve, reject) {
    
    
        setTimeout(() => {
    
    
            //异步操作
            resolve('success1')
        }, 2000);
    })
}).then(function (data) {
    
    
    console.log(data);//success1
    return new Promise(function (resolve, reject) {
    
    
        setTimeout(() => {
    
    
            //异步操作
            resolve('success2')
        }, 2000);
    })
}).then(function (data) {
    
    
    console.log(data);//success2
    setTimeout(() => {
    
    
        //异步操作
    }, 2000);
})

三.生成器(Generators/ yield)

生成器的实质还是使用promise生成器执行器来执行异步程序.
生成器的使用:
首先需要封装一个函数,这个函数内部返回promise对象,在promise对象内部执行异步操作,通过生成器来保留状态.再调用执行器执行.
上面的程序写法如下:

function test(){
    
    
    return new Promise(function(resolve,reject){
    
    
        setTimeout(() => {
    
    
            //异步操作
            resolve();
        }, 2000);
    })
}
//生成器
function* gen(){
    
    
    yield test();
    yield test();
    yield test();
    yield test();
}
//执行器
let it =gen();
it.next().value.then(()=>{
    
    
    return it.next().value;
}).then(()=>{
    
    
    return it.next().value;
}).then(()=>{
    
    
    return it.next().value;
}).then(()=>{
    
    
    return it.next().value;})

通过生成器在一定程度上简化来promise操作,但是手动迭代还是很麻烦的,在2013年的时候,推出了一个co模块,使用co模块就可以自动的执行生成器.

//执行器
co(gen)

四.async/await

async/await是语法糖,其实质仍是生成器.async表示执行的函数里面有异步操作,在语义上能够清晰明了的看到.
async/await的使用:
创建一个返回Promise的函数,在promise内部进行异步操作,再生成一个带有异步操作的函数,需要声明async,函数内部需要进行异步的操作前加上await,await后面的代码必须在异步操作结束以后才能执行.
上面的代码用aync/await写法如下:
注意:asyn执行后返回的是一个promise对象,await表示后面的程序需要等待结束以后才能执行.

function test(){
    
    
    return new Promise(function(resolve,reject){
    
    
        setTimeout(() => {
    
    
            //异步操作
            resolve();
        }, 2000);
    })
}

async function read(){
    
    
    await test();
    await test();
    await test();
    await test();
}

read();

五.总结

1.实现异步的方案从最早的回调函数=>promise=>生成器=>async/await,是在一步一步的基础上改进过来的,没有十全十美的方法,只有不断改进的方案.
2.从目前的情况来看,async/await是异步的最终解决方案.同样的,在不同场景下,每个方法有自己独特的优势,最终的决定权还是每一位开发者.

猜你喜欢

转载自blog.csdn.net/qq_38870665/article/details/108653323
今日推荐