浅谈JS异步(asychrouous)
一、概念
(1)asychronous 异步
是JS这种单线程语言解决多任务的一种方法,将耗时的任务(io)设定为异步工作,先交给浏览器负责相关功能的线程来实现耗时的部分工作,按顺序放入任务队列中,等待主代码执行完成,而主线程继续下一个任务,不阻塞代码;主线程任务完成后,会收到浏览器的通知,来实现异步任务的其他代码执行
===>事件循环+任务队列
(2)sychronous 同步
任务的执行是一个接着一个执行的,上一个任务执行完成才能继续下一个任务,有多个窗口,进行一个接一个的执行任务
二、异步有序问题
1、回调=====>回调地狱
当我们需要进行有序任务时,最粗暴的方式就是用时间函数嵌套,少个还可以分辨,但若出现上百、上千个事件呢?如果任然使用这种方法,就会形成“回调地狱”,没有办法阅读处时间函数的时间对应的代码是哪一个,不利于后续的维护:
setTimeout(function () { console.log("a"); setTimeout(function () { console.log("b"); setTimeout(function () { console.log("c"); setTimeout(function () { console.log("d"); }, 1000) }, 2000) }, 1000) }, 1000)
于是乎,我们的先人们捣鼓出了专门处理异步的方法。
2、Promise 构造函数
首先,我们来看一看使用Promise 构造函数与上面的代码有什么不同
new Promise(function (resolve, reject) { // console.log("a1"); setTimeout(function () { console.log("a"); resolve(); }, 1000) }).then(() => { // return 1;//下一个then被处罚时可以接收返回值 return new Promise(function (resolve, reject) { // console.log("b1"); setTimeout(function () { console.log("b"); resolve(); }, 2000) }) }).then((data) => { return new Promise(function (resolve, reject) { // console.log("c1"); setTimeout(function () { console.log("c",data); resolve(); },3000) }) })
我们可以看到,Promise让每一个异步都是平行的了,不再是嵌套关系,语义化非常强,让人一眼可以看出每个事件发生的顺序。
(1)使用Promise要注意状态值:
- pending 已就绪, Promise实例化的同时就已就绪,状态只可改变1次,且不可逆
- resolved/fulfilled 已完成
- rejected 已失败
===>状态改变,then方法会触发onfulfilled,onrejected事件
(2)Promise构造函数
我们分步骤来看,是怎样应用的:
new Promise(function(resolve,reject){});
let fn1; let fn2; new Promise(function(resolve,reject){//两个形参,都是回调函数 resolve(); fn1 = resolve; fn2 = reject; });
resolve()方法会返回已完成状态,就会触发onfulfilled事件,让then()代码走下去
若要写异步函数,则将第一个异步函数写在Promise里面,之后每一个任务可以使用then()来触发,每个then()只能完成一个任务
p.then((data)=>{//参数为2个事件监听器,第一个是已成功时触发,第二个是以失败时触发 console.log("onfulfilled",data); },(data)=>{ console.log("onrejected",data);//onrejected 触发且异常,不报错 }) console.log(p);
then :
- 返回值为promise对象,初始状态是pending
- 监听器被触发,监听函数执行完成后,返回值的状态发生改变
- 改变的情况由监听函数的执行返回值决定,而具体情况参照Promise.resolve(返回值)
then(onfulfilled,onrejected)含有两个参数,都是事件监听器(函数),第一个是已成功时触发,第二个是以失败时触发。onrejected 触发且异常时,不报错。
整体的代码就是:
let fn1; let fn2; let p = new Promise(function(resolve,reject){//两个形参,都是回调函数 resolve(); //第一个异步函数放在Promise里 fn1 = resolve; fn2 = reject; }); p.then((data)=>{//参数为2个事件监听器,第一个是已成功时触发,第二个是以失败时触发 console.log("onfulfilled",data); },(data)=>{ console.log("onrejected",data);//onrejected 触发且异常,不报错 }) console.log(p);
(3)all(),race()
在Promise中有两个好用的方法,就是 all(),race()
- all()方法:在所有异步代码都跑了后,同时输出结果,但结果显示顺序根据异步事件结束的先后来的
let p1 = new Promise(function (resolve, reject) { console.log("a开始了"); setTimeout(function () { console.log("a"); resolve(); }, 4000) }) let p2 = new Promise(function (resolve, reject) { console.log("b开始了"); setTimeout(function () { console.log("b"); resolve(); }, 3500) }) let p3 = new Promise(function (resolve, reject) { console.log("c开始了"); setTimeout(function () { console.log("c"); resolve(); }, 5000) }) let pp = Promise.all([p1,p2,p3]); pp.then(()=>{ console.log("all"); })
- race()方法:赛跑,谁先结束谁先输出
let p1 = new Promise(function (resolve, reject) { console.log("a开始了"); setTimeout(function () { console.log("a"); resolve(); }, 4000) }) let p2 = new Promise(function (resolve, reject) { console.log("b开始了"); setTimeout(function () { console.log("b"); resolve(); }, 3500) }) let p3 = new Promise(function (resolve, reject) { console.log("c开始了"); setTimeout(function () { console.log("c"); resolve(); }, 5000) }) // let pp = Promise.all([p1,p2,p3]); // pp.then(()=>{ // console.log("all"); // }) pp = Promise.race([p1,p2,p3]); pp.then(()=>{ console.log("race"); })
b时间最短,因此是b获胜
all、race 都生成promise对象,但是状态的变化情况不一样
(4)返回值 return
Promise.value 通过resolve传递的参数
Promise.reslove(value)方法,返回一个以给定值解析后的Promise对象
value有三种情况:
- 普通值:除了promise对象,和thenable对象的所有值,生成一个已完成的promise对象并且PromiseValue的值就是传入的value
- promise对象,直接返回该value
- thenable对象,参照new Promise 生成的promise对象,其中then方法就好比new Promise(cb)中的cb
在then(data)中,data就是value的值,我们回到开头的那段代码:
new Promise(function (resolve, reject) { // console.log("a1"); setTimeout(function () { console.log("a"); resolve(); }, 1000) }).then(() => { // return 1;//下一个then被处罚时可以接收返回值 return new Promise(function (resolve, reject) { // console.log("b1"); setTimeout(function () { console.log("b"); resolve(); }, 2000) }) }).then((data) => { return new Promise(function (resolve, reject) { // console.log("c1"); setTimeout(function () { console.log("c",data); resolve(); },3000) }) })
then(() => {
// return 1;//下一个then被处罚时可以接收返回值,大家可以自己去试一试。