赞
踩
异步编程其实就是处理异步任务,在进行异步编程之前,我们需要了解JavaScript是单线程的,在同一时间只能做一件事。
JavaScript之所以设计成单线程,是与它的用途有关。作为浏览器脚本语言,JavaScript的主要用途是与用户互动以及操作DOM。这决定了它只能是单线程,否则会带来很多复杂的同步问题。例如,如果JavaScript同时有两个线程,一个线程在某个DOM节点上添加内容,另一个线程删除了这个节点,这时浏览器就不晓得以哪个线程为准。所以,为了避免复杂,从一诞生,JavaScript就是单线程的。
单线程就意味着,所有任务都需要排队,前一个任务结束,后一个任务才会执行。那么如果前一个任务很长的话,那么后面一个任务不是就一直需要等待了吗?于是乎,JS将所有任务分成了两类,同步和异步。
同步:只有前一个任务执行完毕,才能执行后一个任务
异步:当同步任务执行到某个需要耗时执行的运行环境API时,就会触发异步任务,此时运行环境(浏览器或Node)就会单独开线程去处理这些异步任务。
下面是JavaScript运行原理图,同步任务在JS主线程完成,异步任务则新开一个线程
疑问:不是说JavaScript是单线程的吗,那为什么又新开了一条线程处理任务呢,这不是多线程方式吗?
有这个疑问不奇怪,我们需要清楚JavaScript单线程其实说的是JavaScript引擎是单线程的,开发者只能通过单线程的方式进行JavaScript开发,而新开了一条线程处理任务是底层执行环境决定的,JavaScript执行环境是多线程。
在实际项目中,异步编程使用场景极其之多,请求个接口数据、创建个定时器、缓存个数据都离不开异步编程的身影,为了更好的处理异步任务,ES6给我们提供两种新的方式,分别是Promise和Generator。
Promise 是一个代理对象,代表了一个异步任务的最终成功或者失败状态。Promise允许你为异步任务的成功或失败分别设置对应的处理方法,以类似同步的方式便捷的进行异步编程。
一个Promise有三种状态:
const promise = new Promise(function (resolve, reject) {
let result=执行异步任务;
if(result){
//如果异步任务成功完成
resolve()
}else{
//如果异步任务执行失败
reject();
}
});
创建Promise对象需要传递一个executor参数,executor是带有resolve和reject两个参数的函数,这两个参数是JavaScript引擎提供的两个函数,Promise构造函数执行会立即调用executor函数。
![image.png](https://img-blog.csdnimg.cn/img_convert/164a56af1903e48ddbc004a29f44f940.png#align=left&display=inline&height=463&margin=[object Object]&name=image.png&originHeight=463&originWidth=759&size=47507&status=done&style=none&width=759)
实例好promise对象后,我们可以使用下面的语法来对异步任务完成后的状态进行处理。
promise.then(onFulfilled,onRejected)
promise.then(
(result) => {
console.log("异步任务处理成功,执行相应方法");
},
(error) => {
console.log("异步任务处理失败,执行相应方法");
}
);
但在具体的项目开发中,我们通常都是使用已经存在的Promise对象,下面我们就通过使用常用的promise对象fetch获取接口数据。
我们需要调用接口,获取一个用户列表数据
fetch("http://jsonplaceholder.typicode.com/users")
.then(function (response) {
return response.json();
})
.then(function (res) {
console.log(res); // [{…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}]
});
response是一个包含响应结果的Response对象,它只是一个HTTP响应,而不是真正的JSON。为了获取JSON的内容,需要使用json()方法获取一个Promise对象,然后再使用then获取JSON数据。
Promise提供了catch()方法,用来捕获异步操作过程中遇到的错误异常,使用场景如下
const CatchPromise = new Promise(function (resolve, reject) {
reject(new Error("error msg"));
});
CatchPromise.then().catch((e) => {
console.error(e); //Error: error msg
});
在Promise对象中,除了可以使用reject(new Error())的方式触发异常,还可以使用throw new Error()的方式触发异常,但不建议使用throw new Error()的方式,因为这种方式不会改变Promise的状态。
Promise.all()用于处理多个异步任务,例如处理多张图片上传。Promise.all()接受一个promise对象数组作为参数,执行完毕返回一个Promise对象。
Promise.all()的状态变化:
传入的promise对象数组全部变为fulfill状态则返回成功,调用resolve()
传入的promise对象数组有一个变为reject状态则返回失败,调用reject()
const promise1 = new Promise(function (resolve, reject) { setTimeout(function () { resolve("promise1"); }, 2000); }); const promise2 = new Promise(function (resolve, reject) { setTimeout(function () { resolve("promise2"); }, 1000); }); const promise3 = new Promise(function (resolve, reject) { setTimeout(function () { resolve("promise3"); }, 3000); }); const promiseAll = Promise.all([promise1, promise2, promise3]); promiseAll.then(function (results) { console.log(results); // ["promise1", "promise2", "promise3"] });
const promiseAll = Promise.race([promise1, promise2, promise3]);
promiseAll.then(function (results) {
console.log(results); // promise2
});
<a name="bOVQP"></a> ## Generator函数 Generator函数是用来处理异步任务的函数,函数内部包裹的就是异步任务的处理。Generator不同于普通函数,当执行到异步任务,可以暂停,直到异步任务执行完毕再继续往下执行,类似同步的方法进行异步编程。<br /> <a name="zRjaF"></a> ### 如何使用 Generator函数在使用上具体有以下特点 - *:定义generator函数时,function后面需要加上星号,例如function* generatorFuncName() - yield:需要暂停去执行异步任务时,需要在代码前面加上yield标识,例如yield fetch() - next():调用generator函数后获得的是一个指针对象,调用指针的next方法,可以用来分阶段逐步执行generator函数。 <br />那么具体如何使用generator函数实现异步编程呢,在学习Promise的时候,我们实现了一个获取一个用户列表数据的案例,下面我们看看如何使用generator函数实现吧。 ```javascript function* loadUsers() { const API = "http://jsonplaceholder.typicode.com/users"; console.log("等待数据请求"); yield fetch(API); //暂停,开始执行异步任务 console.log("数据请求完成"); console.log("继续其他逻辑操作"); } const generator = loadUsers(); const promise = generator.next().value; console.log(promise); promise .then(function (response) { return response.json(); }) .then(function (result) { console.log(result); //[{…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}] generator.next(); //打印:数据请求完成 继续其他逻辑操作。异步任务执行完毕后调用next(),继续执行generator函数中后续代码 });
更多文章内容,请关注公众号【方塘HCI】
参考资料
【1】JavaScript 运行原理解析
【2】JS是单线程,你了解其运行机制吗?
【3】史上最最最详细的手写Promise教程
【4】Generator 函数的含义与用法
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。