同步、异步及Promise

同步、异步及Promise,第1张

什么是同步、异步? 1. 同步

JavaScript是一门单线程的语言,因此,JavaScript在同一个时间只能做一件事,单线程意味着,如果在同个时间有多个任务的话,这些任务就需要进行排队,前一个任务执行完,才会执行下一个任务,会造成运行阻塞,严重影响用户体验。

2. 异步

异步任务是指不进入主线程,而进入任务队列的任务,只有任务队列通知主线程,某个异步任务可以执行了,该任务才会进入主线程,例如图片、音乐的加载,http请求等。

注意:异步任务又分为微任务、宏任务。
微任务包括:

Promise.then
Promise.catch
Promise.finally
Object.observe
MutationObserver
process.nextTick(Vue、Node.js 环境)

宏任务包括:

script(整体代码)
setTimeout
setInterval
I/O
UI交互事件
postMessage
MessageChannel
setImmediate(Node.js 环境)
Promise 1) 什么是Promise?

接下来请注意蓝色字体部分,能帮助您更快阅读和理解本文章~

Promise是异步编程的一种解决方案,也是异步回调的语法糖,可用于解决回调地狱的问题,并支持多个并发的请求,我们平时使用的axios就是基于Promise进行了封装。Promise其实是一个构造函数,自己身上有resolve、reject、all、race这几个方法,原型上有then、catch等方法,它将原来的回调写法分离出来,在异步 *** 作完成后采用链式调用的方式执行回调函数。
Promise对象有三种状态:pending(进行中)、fulfilled(已成功)、rejected(已失败)。可通过resolve、reject方法改变状态,并且只有两种可能:从pending变为fulfilled、从pending变为rejected,一旦状态改变,就不会再变,任何时候都可以得到这个结果。

注意:下列解析各个场景

当Promise中同时resolve与reject,那么先写的先执行,后面的resolve或reject不再执行(其他代码依旧会执行,不管是同步还是异步)。then、catch、finally回调是异步的(微任务),所以会等待Promise中的同步代码执行完成才会执行then、catch、finally中的代码,不管resolve与reject在Promise中何时执行。 2) resolve

在Promise对象中调用resolve方法,将执行then方法的第一个回调,我们就能捕捉到执行成功的情况,并将Promise对象的状态由pending变为fulfilled。

3) reject

在Promise对象中调用reject方法,将执行then方法的第二个回调,我们就能捕捉到执行失败的情况,并将Promise对象的状态由pending变为rejected。

4) then

then方法有两个回调,第一个对应resolve的回调,第二个对应reject的回调。(也就是说then方法中接受两个回调,一个成功的回调函数,一个失败的回调函数,并且能在回调函数中拿到成功的数据和失败的原因)

function promiseClick(){
    let p = new Promise(function(resolve, reject){
        setTimeout(function(){
            //生成1-10的随机数
            var num = Math.ceil(Math.random()*20); 
            console.log('随机数生成的值:',num)
            if(num<=10){
                resolve(num);
            }
            else{
                reject('数字太于10了即将执行失败回调');
            }
        }, 2000);
    })
    return p
};
promiseClick().then(
    function(data){
        console.log('resolved成功回调');
        console.log('成功回调接受的值:',data);
	}, 
	function(reason){
		console.log('rejected失败回调');
		console.log('失败执行回调抛出失败原因:',reason);
	}
);

执行结果:

5) catch

与try catch类似,catch就是用来捕获异常的,它有两个作用:
① 它和then方法中第二个回调是一样的,如果then方法中没有写第二个回调,那么catch方法就会捕获reject出来的值;
② 在执行resolve的回调(也就是上面then中的第一个参数)时,如果抛出异常了(throw new Error( ))或代码出错了,那么并不会报错卡死js,而是会进到这个catch方法中。

function promiseClick(){
    let p = new Promise(function(resolve, reject){
        setTimeout(function(){
            //生成1-10的随机数
            var num = Math.ceil(Math.random()*20); 
            console.log('随机数生成的值:',num)
            if(num<=10){
                resolve(num);
            }
            else{
                reject('数字太于10了即将执行失败回调');
            }
        }, 2000);
    })
    return p
};
promiseClick().then(
    function(data){
        console.log('resolved成功回调');
        console.log('成功回调接受的值:',data);
        console.log(noData);
	}
).catch(
    function(reason){
	    console.log('catch到rejected失败回调');
        console.log('catch失败执行回调抛出失败原因:',reason);
    }
);

第一种情况执行结果:

第二种情况执行结果:

6) finally

与try catch finally类似,该方法用于指定不管Promise对象最后状态如何,都会执行的 *** 作。

function promiseClick(){
    let p = new Promise(function(resolve, reject){
        setTimeout(function(){
            //生成1-10的随机数
            var num = Math.ceil(Math.random()*20); 
            console.log('随机数生成的值:',num)
            if(num<=10){
                resolve(num);
            }
            else{
                reject('数字太于10了即将执行失败回调');
            }
        }, 2000);
    })
    return p
};
promiseClick().then(
    function(data){
        console.log('resolved成功回调');
        console.log('成功回调接受的值:',data);
    }
).catch(
    function(reason){
	    console.log('catch到rejected失败回调');
        console.log('catch失败执行回调抛出失败原因:',reason);
    }
).finally(
    function(){
	    console.log('无论怎样都要执行的代码');
    }
);

执行结果:

7) all

我们可能会遇到这样的场景:比如说一个页面上需要等两个或多个ajax的数据回来以后才正常显示,在此之前只显示loading图标。这个时候Promise.all方法就发挥它独特的魅力。
Promise.all可以将多个Promise实例包装成一个新的Promise实例。该方法提供了并行执行异步 *** 作的能力,并且在所有异步 *** 作执行完后且执行结果都是成功的时候才执行then方法第一个回调(all统一执行完所有Promise并将resolve的值存在一个数组里面返回给then方法),当有一个Promise失败的时候执行then方法的第二个回调或catch方法的回调,且只能捕获第一个失败的Promise的错误,其余的Promise还会继续执行,但不进入任何回调(不管成功还是失败)。

function promiseClick1(){
    let p = new Promise(function(resolve, reject){
        setTimeout(function(){
            //生成1-10的随机数
            var num = Math.ceil(Math.random()*20); 
            console.log('随机数生成的值:',num)
            if(num<=10){
                resolve(num);
            }
            else{
                reject('数字太于10了即将执行失败回调');
            }
        }, 2000);
    })
    return p
};
function promiseClick2(){
    let p = new Promise(function(resolve, reject){
        setTimeout(function(){
            //生成1-10的随机数
            var num = Math.ceil(Math.random()*20); 
            console.log('随机数生成的值:',num)
            if(num<=10){
                resolve(num);
            }
            else{
                reject('数字太于10了即将执行失败回调');
            }
        }, 2000);
    })
    return p
};
function promiseClick3(){
    let p = new Promise(function(resolve, reject){
        setTimeout(function(){
            //生成1-10的随机数
            var num = Math.ceil(Math.random()*20); 
            console.log('随机数生成的值:',num)
            if(num<=10){
                resolve(num);
            }
            else{
                reject('数字太于10了即将执行失败回调');
            }
        }, 2000);
    })
    return p
};
Promise.all([promiseClick3(), promiseClick2(), promiseClick1()])
    .then(function(results){
        // 全部Promise执行完成且都成功时进入
        console.log(results);
    }).catch(function(error){
        // 首个异步操作执行失败时进入,后续Promise执行失败将不会再进入
        console.log(error);
    });

执行结果:
成功:

失败:

需要特别注意的是,Promise.all获得的成功结果的数组里面的数据顺序和Promise.all接收到的数组顺序是一致的,即p1的结果在前,即便p1的结果获取的比p2要晚。这带来了一个绝大的好处:在前端开发请求数据的过程中,偶尔会遇到发送多个请求并根据请求顺序获取和使用数据的场景,使用Promise.all毫无疑问可以解决这个问题。

8) race

既然all是并行执行异步 *** 作,那么我们应该猜到race是干嘛的,顾名思义,race意为“赛跑”,谁先执行完成就先执行回调。先执行完的不管是进行了race的成功回调还是失败回调,其余的Promise会继续执行,但不会再进入race的任何回调。

function promiseClick1(){
    let p = new Promise(function(resolve, reject){
        setTimeout(function(){
            //生成1-10的随机数
            var num = Math.ceil(Math.random()*20); 
            console.log('随机数生成的值:',num)
            if(num<=10){
                resolve(num);
            }
            else{
                reject('数字太于10了即将执行失败回调');
            }
        }, 2000);
    })
    return p
};
function promiseClick2(){
    let p = new Promise(function(resolve, reject){
        setTimeout(function(){
            //生成1-10的随机数
            var num = Math.ceil(Math.random()*20); 
            console.log('随机数生成的值:',num)
            if(num<=10){
                resolve(num);
            }
            else{
                reject('数字太于10了即将执行失败回调');
            }
        }, 3000);
    })
    return p
};
function promiseClick3(){
    let p = new Promise(function(resolve, reject){
        setTimeout(function(){
            //生成1-10的随机数
            var num = Math.ceil(Math.random()*20); 
            console.log('随机数生成的值:',num)
            if(num<=10){
                resolve(num);
            }
            else{
                reject('数字太于10了即将执行失败回调');
            }
        }, 4000);
    })
    return p
};
Promise.race([promiseClick3(), promiseClick2(), promiseClick1()])
    .then(function(results){
        // 只要有一个Promise执行成功就进入,后续的Promise执行成功将不会再进入
        console.log(results);
    }).catch(function(error){
        // 只要有一个Promise执行失败就进入,后续的Promise执行失败将不会再进入
        console.log(error);
    });

执行结果:
成功:

失败:

race的使用场景:自定义限制请求超时时间,例如我想在10s内请求成功的话就走then方法,否则进入reject的回调。

//请求某个table数据
function requestTableList(){
    var p = new Promise((resolve, reject) => {
    //去后台请求数据,这里可以是ajax,可以是axios,可以是fetch 
        resolve(res);
    });
    return p;
}
//延时函数,用于给请求计时 10s
function timeout(){
    var p = new Promise((resolve, reject) => {
        setTimeout(() => {
            reject('请求超时');
        }, 10000);
    });
    return p;
}
Promise.race([requestTableList(), timeout()])
    .then((data) =>{
        //进行成功回调处理
        console.log(data);
    }).catch((err) => {
        // 失败回调处理
        console.log(err);
    });

谢谢各位抬爱~~

欢迎分享,转载请注明来源:内存溢出

原文地址: http://outofmemory.cn/web/1319768.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022-06-11
下一篇 2022-06-11

发表评论

登录后才能评论

评论列表(0条)

保存