JavaScript异步编程(回调函数,Promise)

本文详细探讨了JavaScript中的异步编程,从同步执行的概念入手,介绍了回调函数及其导致的‘回调地狱’问题。接着,文章重点讨论了Promise如何解决这些问题,包括Promise的三种状态、数据传递、Promise.all、Promise.race以及异常处理机制。通过对Promise的深入解析,帮助读者更好地理解和应用异步编程。

谈起异步编程,我们先来了解下同步执行…如下
同步执行:同步执行不是代码同步执行,而是代码依次执行,后一个任务必须等前一个任务执行完后再执行。执行方式就是代码写的顺序。
同步请求,提交请求->等待服务器处理->处理完毕返回 这个期间客户端浏览器不能干任何事。

	console.log("打印console")
	function f1(){
	    console.log("f1函数")
	}
	function f2(){
	    console.log("f2函数")
	    f1()
	}
	f2()
	f1()
	//输出结果:打印console,f2函数,f1函数,f1函数

异步执行:不会等待当前任务执行结束后才会执行下一个任务,当前任务开启后就会往下执行。谁先执行完算谁的
异步请求请求通过事件触发->服务器处理(这是浏览器仍然可以作其他事情)->处理完毕

	console.log("start demo3") //执行1
	setTimeout(() => {
	    console.log("5秒后执行") //执行5
	}, 5000);
	setTimeout(() => {
	    console.log("3秒后执行") //执行3
	    setTimeout(() => {
	        console.log("1秒后执行")  //执行4
	    }, 1000);
	}, 3000);
	console.log("end demo3")  //执行2
	//输出结果:start demo3,end demo3,3秒后执行,1秒后执行,5秒后执行

有兴趣的可以看看这个执行流程图:
有兴趣的可以看看这个执行流程图

一:回调函数

一个函数作为参数传递给另外一个函数(另一个函数我们称为:otherFunction),回调函数就在otherFunction中调用
最简单的demo:
定时器里的匿名函数function就是一个回调函数。(js中的函数是一等公民,所以可以像其他变量一样作为参数进行传递)

setTimeout(function(){
	console.log('time out')
},1000)

这样看来,用回调函数处理异步也挺好的,但是需求复杂时,(例子:某个业务依赖于上层业务的数据,上层业务又依赖于更上一层的数据)整个代码会充满了嵌套,读起来麻烦,也不易于扩展。俗称为“回调地狱”

$.ajax({
    type: 'get',
    url: '/api/getKey',
    success: function (data) {
        key = data;
        $.ajax({
            type: 'get',
            url: '/api/getToken',
            data: {
                key: key
            },
            success: function (data) {
                token = data.token;
                userId = data.userId;
                $.ajax({
                    type: 'get',
                    url: '/api/getData',
                    data: {
                        token: token,
                        userId: userId
                    },
                    success: function (data) {
                        console.log('业务数据:', data);
                    },
                    error: function (err) {
                        console.log(err);
                    }
                });
            },
            error: function (err) {
                console.log(err);
            }
        });
    },
    error: function (err) {
        console.log(err);
    }
});

综上:回调函数处理异步的2个问题:

  • 1.缺乏顺序性,回调地狱导致 调试困难
  • 2.缺乏可信任性,控制反转导致的一系列信任问题

二:Promise

promise解决回调函数处理异步的问题
上面代码用Promise优化为:

//获取key值
let getKeyPromise = function () {
    return new Promise(function (resolve , reject){
        $.ajax({
            type: 'get',
            url: '/api/getKey',
            success: function (data) {
               let key = data;
               resolve(key);         
            },
            error: function (err) {
                reject(err);
            }
        });
    });
};
//获取token,参数传递key值
let getTokenPromise = function (key) {
    return new Promsie(function (resolve, reject) {
        $.ajax({
            type: 'get',
            url: '/api/getToken',
            data: {
                key: key
            },
            success: function (data) {
                resolve(data);         
            },
            error: function (err) {
                reject(err);
            }
        });
    });
};
// 请求业务数据,参数传递key和token
let getDataPromise = function (data) {
    let token = data.token;
    let userId = data.userId;
    
    return new Promsie(function (resolve, reject) {
        $.ajax({
            type: 'get',
            url: '/api/getData',
            data: {
                token: token,
                userId: userId
            },
            success: function (data) {
                resolve(data);         
            },
            error: function (err) {
                reject(err);
            }
        });
    });
};
调用getKeyPromise()方法,依次then调用下面方法
getKeyPromise()
    .then(function (key) {
        return getTokenPromise(key);
    })
    .then(function (data) {
        return getDataPromise(data);
    })
    .then(function (data) {
        console.log('业务数据:', data);
    })
    .catch(function (err) {
        console.log(err);
    }); 
  • Promise对象的then方法会返回一个全新的Promise对象
  • 后面的then方法就是在为上一个then返回的Promise注册对应的回调
  • 前面then方法中回调函数的返回值会作为后面then方法回调的参数
  • 如果回调中返回的是Promise,那后面then方法的回调会等待它的结束
	function ajax(url){
	    return new Promise(function(resolve,reject){
	        const xhr = new XMLHttpRequest()
	        xhr.open('GET',url)
	        xhr.responseType='json'
	        xhr.onload = function(){
	            console.log(this.status)
	            if(this.status == 200){
	                console.log("请求成功")
	                resolve(this.response)
	            }else{
	                console.log("请求失败")
	                reject(new Error(this.statusText))
	            }
	        }
	        xhr.send()
	    })
	}
	ajax('/api/wyy.json').then(function(value){
	    console.log(111)
	    console.log(value)
	    return ajax('wyy/w1.json')
	}) // => Promise {}
	.then(function(value){
	    console.log(222)
	    console.log(value)
	    return ajax('wyy/w2.json')
	}) // => Promise {}
	.then(function(value){
	    console.log(333)
	    console.log(value)
	    return ajax('wyy/w3.json')
	})

执行结果:
在这里插入图片描述

1. Promise对象有三种状态:

  • pending:等待中/进行中,表示还没有得到结果
  • resolved( fulfilled ):已经完成,表示得到了我们想要的结果,可以继续往下执行
  • rejected:也表示得到了结果,但是结果不是我们想要的,不会往下执行或者执行catch

在Promise对象的构造函数中,将一个函数作为第一个参数,而这个函数,就是用来处理Promise的状态变化的。

	new Promise(function(resolve,reject){
	    if(true){console.log('成功');resolve()}
	    if(false){console.log('失败');reject()}
	})

resolve,reject都为一个函数,作用分别是将状态修改为resolved和rejected。

2. Promise中的数据传递:

	var fn = function(num) {
	    return new Promise(function(resolve, reject) {
	        if (typeof num == 'number') {
	            resolve(num);
	        } else {
	            reject('TypeError');
	        }
	    })
	}
	
	fn(2).then(function(num) {
	    console.log('first: ' + num);
	    return num + 1;
	})
	.then(function(num) {
	    console.log('second: ' + num);
	    return num + 1;
	})
	.then(function(num) {
	    console.log('third: ' + num);
	    return num + 1;
	});
	//打印结果:first: 2  second: 3  third: 4

现在所有的库几乎都将ajax请求利用Promise进行封装了。下面简单封装个小demo,以供参考:

// 封装一个get请求的方法
	function getJSON(url) {
	    return new Promise(function(resolve, reject) {
	        var XHR = new XMLHttpRequest();
	        XHR.open('GET', url, true);
	        XHR.send();
	
	        XHR.onreadystatechange = function() {
	            if (XHR.readyState == 4) {
	                if (XHR.status == 200) {
	                    try {
	                        var response = JSON.parse(XHR.responseText);
	                        resolve(response);
	                    } catch (e) {
	                        reject(e);
	                    }
	                } else {
	                    console.log("错误打印:")
	                    reject(new Error(XHR.statusText));
	                }
	            }
	        }
	    })
	}
	var url = 'https://api/wyy_stydy.com';
	getJSON(url).then(res => console.log(res));

3. Promise.all

当一个ajax请求,它的参数需要多个请求返回的结果之后才能确定,那么就需要用Promise.all了
Promise.all接收一个promise对象组成的数组为参数,当这个数组所有的promise对象状态都编程resolved或则rejected时,才会调用then方法

	var promiseAll = Promise.all([  // Promise.all() 等待所有任务结束
	    ajax('/api/dudu1.json'),
	    ajax('/api/dudu2.json'),
	    ajax('/api/dudu3.json'),
	])
	promiseAll.then(function(value){
	    console.log(value) //等待所有任务结束后才会执行
	}).catch(function(error){
	    console.log(error) //等待所有任务结束后才会执行
	})
let urlsArr = {https1:'/api/wei1.json',https2:'/api/wei2.json',https3:'/api/wei3.json'}
	ajax(urlsArr)
	    .then(value => {
	        const urls = Object.values(urlsArr)
	        console.log('urls:' , urls)
	        //打印:["/api/wei1.json", "/api/wei2.json", "/api/wei3.json"]
	        const task = urls.map(url => ajax(url))
	        return Promise.all(task)
	    }).then(values => {
	        console.log(values)
	    })

3. Promise.race

与Promise.all类似,都是以一个Promise对象组成的数组作为参数;不同的是当一个Promise对象返回rejected或者resolved时,就会执行.then方法了。

4. Promise捕获异常

catch捕获异常:通过 catch 方式给整个 promise 链条注册失败回调,promise 具有传递性
then方法指定失败回调函数:通过 then 方法的第二个参数指定失败的回调函数,它只能够捕获到前一个 promise 的异常,会出现无法捕获 promise 异常的错误

	promise.then((res)=>{
	    console.log('then' , res)
	}).catch((err)=>{
	    console.log('catch:' , err) //所有then异常都能捕获
	})
	promise.then((res) => {
	    console.log('then:' , res)
	},(err)=>{
	    console.log('error:',err) //只能捕获上一个then的异常
	})
未完待续... 后面还会持续更新一些Promise使用场景...
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值