promise、async、await解析

对于js来说,由于本身就是单线程,在执行耗时操作的时候,比如网络请求等。如果使用同步的话,那么会阻塞整个线程,这样的其实是不合理的。在node中提出了一个很重要的特性,非阻塞式 I/O 的模型、而实现这种模型的基础就是基于事件回调机制。

普通的回调模型

如果使用普通的方式进行回调监听的话,有时候在回调函数中又有回调函数,这样的话会造成回调地狱,对于后期代码的阅读和维护增加很大的难度。

XMLHttpRequest request = new XMLHttpRequest();
request.onreadystatechange = function() {
	if (request.status === 200 && request.readyState === 4) {
		// success
		document.getElementById('sendBtn').addEventListener('click', function(){
			console.log('button is clicked...');
		})
	}
};
request.open('GET', 'https://www.baidu.com', true);
request.send(null);
Promise

promise其实就是为了解决js中异步回调的问题。避免在函数中继续回调,采用了链式的写法,大大提高可阅读行。

let promise = new Promise((resolve, reject) => {
	XMLHttpRequest request = new XMLHttpRequest();
	request.onreadystatechange = function() {
		if (request.status === 200 && request.readyState === 4) {
			// success
			resolve();
		}else {
			reject();
		}
	};
	request.open('GET', 'https://www.baidu.com', true);
	request.send(null);
});
promise.then(function() {
	document.getElementById('sendBtn').addEventListener('click', function(){
		console.log('button is clicked...');
	})
}).catch(function(err){
	console.log(err);
});
链式调用

promise的then默认会返回一个新的promise对象,所以在then函数中可以实现链式调用。如果要让p1在p2执行完成之后执行的话,那么可以在then函数中返回p1对象即可。

const p1 = new Promise(function (resolve, reject) {
  resolve('p1');
});

const p2 = new Promise(function (resolve, reject) {
  resolve('p2');
})
p2.then((data) => {
	console.log(data);
	return p1;
}).then((data)=>{
	console.log(data);
})
对于异常的处理

在promise中抛出异常的话,不会终止程序的运行。如果抛出异常,则相当于执行了reject,那么后面的resolve不会修改promise的状态,此时的promise状态已经是reject,不会再被修改。

let promise = new Promise((resolve, reject) => {
	console.log('script start');
	throw new Error('error');
	resolve('success');
})
promise.then((data)=>{
	console.log(data);
}).catch((err)=> {
	console.log(err);
})
console.log('script end...');
promise.all理解

promise.all会返回一个promise对象,而最关键的就是在最后一个promise任务的then函数中调用该返回的promise对象的resolve告知所有的任务已经执行完毕。

Promise.all = function (...arg) {
    return new Promise((resolve, reject) => {
        for (let index=0; index<arg.length; index++) {
            (function (i) {
                Promise.resolve(arg[i]).then(function (result) {
                    if (i === arg.length-1) {
                        resolve(result);
                    }
                }).catch(function (err) {
                    reject(err);
                })
            })(index);
        }
    });
};
let promise1 = new Promise(((resolve, reject) => {
    console.log('promise1');
    resolve();
}));
let promise2 = new Promise(((resolve, reject) => {
    console.log('promise2');
    resolve();
}));
Promise.all([promise1, promise2]);
Promise其他的静态函数的理解
  • Promise.resolve
    如果resolve的参数是一个promise的实例的话,那么会将该对象直接返回。
    如果是数字或者字符串类型的话,那么会将该基本类型包装成一个promise的实例,主要是为了能够让其调用then进行相应的回调。可以利用这个特性将一个任务添加到微任务队列中,在同步代码执行完毕之后在执行该任务。
Promise.resolve('1')
// 等价于
new Promise(resolve => resolve('1'))
Promise总结
  • 在创建promise实例的时候,传递给构造函数的方法中的代码是会立即执行的。遇到resolve或者reject的话,则会将其放置到微任务队列中,继续向下执行,在同步代码执行完毕之后,任务队列会从微任务队列中取出resolve或者reject,然后执行相应的then方法。resolve,reject并不会阻塞后面的同步代码。
new Promise(resolve => {
    console.log('main thread'); // 立即执行
    resolve();// 放入到微任务队列中,等同步代码执行完毕之后在执行对应的then
}).then(() => {
    console.log('2...');
})
console.log('1....');
// 执行结果:
// main thread
// 1....
// 2...
async、await讲解

虽然promise利用了resolve、reject解决了回调函数多次嵌套的问题,但是async、await直接将异步回调采用了同步代码的写法,写法上更贴近同步代码的写法。 下面是基本的用法,其实也是对promise进行一层封装。函数返回一个promise对象给await,await会将resolve中的结果进行返回,达到了将异步代码使用同步的写法。

function request() {
    return new Promise((resolve, reject) => {
        resolve('resolve data....');
    });
}
async function test() {
    let data = await request();
    console.log(data);
}
test();
async的作用

从下面的代码可以看出,对于async函数,函数返回的是一个promise对象,除了这个其他跟普通的函数其实没什么区别。使用async只是标示该方法中有await。

let fn1 = async function() {
    return 1;
}
console.log(fn1());
let fn2 = function fn() {
    return 1
}
console.log(fn2());
fn1执行结果
Promise {<resolved>: 1}
__proto__: Promise
[[PromiseStatus]]: "resolved"
[[PromiseValue]]: 1
fn2执行结果
1
await的作用

await主要是等待右边的表达式的执行结果。对于表达式的结果有两种情况。

  • 结果是一个promise对象。在执行完右边的表达式之后,await会让出线程,await后面的代码则会被阻塞,正是这个原因,才能实现将异步的代码使用同步的写法。让出线程后会执行脚本中的其他同步代码,等同步执行完毕之后,再回到await,此时还不能继续执行,因为此时的返回值是promise对象,所以必须等到该promise对象的状态变为resolve或者reject的时候,await才会返回相应的值,然后继续执行后面的代码。
  • 如果返回值不是一个promise对象。此时await也是让出线程,因为只有这样才不会执行await后面的代码,继续执行其他的同步代码,完毕之后回到await,因为此时不是promise对象,相当于是Promise.resolve(obj),那么await则会返回相应的值,然后继续执行后续的代码。
优先级比较
  • promise
    当一个promise对象有多个then回调的话,实际上每一次的then函数都会返回一个新的promise的实例,已经不在是原来的promise实例。所以也就是为什么先打印2在打印3。创建promise1,打印’createa promise obj 1…’,此时执行resolve,则会将任务放置到微任务队列中,线程继续向下执行。创建promise2,打印’createa promise obj 2…’,执行resolve,同样将任务放置到微任务队列中。到此同步代码执行完毕。接下来执行微任务队列中的任务,第一个promise1的resolve任务,所以会调用对应的then方法,打印’1…’,由于then函数会返回一个promise对象,可以理解成Promise.resolve().then(()=>{console.log(‘3…’)}),当前的promise对象的状态只是resolve,但是还是不会执行then方法,只会将其放置到微任务队列中。然后又从微任务队列中取出promise2的resolve,此时会打印’2…’,最后就是刚才的’3…‘对应的promise对象的resolve,取出该任务之后打印’3…’,完毕。这里其实也可以看成是Promise.resolve的一个应用。
let promise1 = new Promise(resolve => {
	console.log('createa promise obj 1...');
	resolve();
}).then(() => {
	console.log('1...');
}).then(() => {
	console.log('3...');
})
let promise2 = new Promise(resolve => {
	console.log('createa promise obj 2...');
	resolve();
}).then(()=>{
	console.log('2...');
});
// 执行结果:
// createa promise obj 1...
// createa promise obj 2...
// 1...
// 2...
// 3...
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值