淘宝内部优惠券网站怎么做,怎么评价网站做的好坏,android手机版,苏中建设集团官方网站解决异步问题——promise、async/await一、单线程和异步1、单线程是什么2、为什么需要异步3、使用异步的场景二、promise1、promise的三种状态2、三种状态的表现和变化#xff08;1#xff09;状态的变化#xff08;2#xff09;状态的表现3、then和catch对状态的影响…
解决异步问题——promise、async/await一、单线程和异步1、单线程是什么2、为什么需要异步3、使用异步的场景二、promise1、promise的三种状态2、三种状态的表现和变化1状态的变化2状态的表现3、then和catch对状态的影响重要4、then和catch的链式调用常考三、async/await1、引例阐述2、async和await3、async/await和promise的关系4、异步的本质5、场景题1async/await语法2async/await的顺序问题四、写在最后我们平常在写 js 中经常只管程序能跑就行但很少去深究它的原理。更别说还有一个同步和异步的问题了。因此程序往往有时候出现莫名其妙的卡死或者有时候执行顺序达不到我们想要的结果时自己都不知道往哪里找错。下面的这篇文章中将讲解同步和异步的问题以及如何解决异步问题的promise、async/await方法。 
在看本文之前建议大家先对 event loop 有一个了解。可以先看我的上一篇文章了解event loop以及微任务宏任务的相关知识这样到后面看本文的 async/await 的时候会更友好一些。 
下面开始进行本文的讲解。 
一、单线程和异步 
1、单线程是什么 
1 JS 是单线程语言只能同时做一件事情 
所谓单线程就是只能同时做一件事情多一件都不行这就是单线程。 
2 浏览器和 nodejs 已支持 JS 启动进程如 Web Worker 
3 JS 和 DOM 渲染共用同一个线程因为 JS 可修改 DOM 结构 
JS 可以修改 DOM 结构使得它们必须共用同一个线程这间接算是一件迫不得已的事情。所以当 DOM 在渲染时 JS 必须停止执行而 JS 执行过程 DOM 渲染也必须停止。 
2、为什么需要异步 
当程序遇到网络请求或定时任务等问题时这个时候会有一个等待时间。 
假设一个定时器设置10s如果放在同步任务里同步任务会阻塞代码执行我们会等待10s后才能看到我们想要的结果。1个定时器的等待时间可能还好如果这个时候是100个定时器呢我们总不能等待着1000s的时间就为了看到我们想要的结果吧这几乎不太现实。 
那么这个时候就需要异步通过异步来让程序不阻塞代码执行灵活执行程序。 
3、使用异步的场景 
1异步的请求如ajax图片加载 
//ajax
console.log(start);
$.get(./data1.json, function (data1) {console.log(data1);
});
console.log(end);2定时任务如setTimeout、setInterval 
//setTimeout
console.log(100);
setTimeout(fucntion(){console.log(200);           
}, 1000);
console.log(300);//setInterval
console.log(100);
setInterval(fucntion(){console.log(200);           
}, 1000);
console.log(300);二、promise 
早期我们在解决异步问题的时候基本上都是使用callback回调函数的形式 来调用的。形式如下 
//获取第一份数据
$.get(url1, (data1)  {console.log(data1);//获取第二份数据$.get(url2, (data2)  {console.log(data2);//获取第三份数据$.get(url3, (data3)  {console.log(data3);//还可以获取更多数据});});
});从上述代码中可以看到早期在调用数据的时候都是一层套一层 callback 调用 callback 仿佛深陷调用地狱一样数据也被调用的非常乱七八糟的。所以因为 callback 对开发如此不友好也就有了后来的 promise 产生 promise 的出现解决了 callback hell 的问题。 
用一段代码先来了解一下 Promise 。 
function getData(url){return new Promise((resolve, reject)  {$.ajax({url,success(data){resolve(data);},error(err){reject(err);}});});
}const url1  /data1.json;
const url2  /data2.json;
const url3  ./data3.json;
getData(url1).then(data1  {console.log(data1);return getData(url2);
}).then(data2  {console.log(data2);return getData(url3);
}).then(data3  {console.log(data3);
}).catch(err  console.error(err)); 
大家可以看到运用了 promise 之后代码不再是一层套一层而是通过 .then 的方式来对数据进行一个获取这在写法上就已经美观了不少。那 promise 究竟是什么呢接下来开始进行讲解。 
1、promise的三种状态 
pending 等待状态即在过程中还没有结果。比如正在网络请求或定时器没有到时间。 
fulfilled 满足状态即事件已经解决了并且成功了当我们主动回调了 fulfilled 时就处于该状态并且会回调 then 函数。rejected 拒绝状态即事件已经被拒绝了也就是失败了当我们主动回调了 reject 时就处于该状态并且会回调 catch 函数。 总结 只会出现pending → fulfilled或者pending → rejected 状态即要么成功要么失败不管是成功的状态还是失败的状态结果都不可逆。 2、三种状态的表现和变化 
1状态的变化 
promise 主要有以上三种状态 pending 、 fulfilled 和 rejected 。当返回一个 pending 状态的 promise 时不会触发 then 和 catch 。当返回一个 fulfilled 状态时会触发 then 回调函数。当返回一个 rejected 状态时会触发 catch 回调函数。那在这几个状态之间他们是怎么变化的呢 
1演示1 
先来看一段代码。 
const p1  new Promise((resolved, rejected)  {});console.log(p1, p1); //pending在以上的这段代码中控制台打印结果如下。 在这段代码中 p1 函数里面没有内容可以执行所以一直在等待状态因此是 pending 。 
2演示2 
const p2  new Promise((resolved, rejected)  {setTimeout(()  {resolved();});
});console.log(p2, p2); //pending 一开始打印时
setTimeout(()  console.log(p2-setTimeout, p2)); //fulfilled在以上的这段代码中控制台打印结果如下。 在这段代码中 p2 一开始打印的是 pending 状态因为它没有执行到 setTimeout 里面。等到后续执行 setTimeout 时才会触发到 resolved 函数触发后返回一个 fulfilled 状态 promise 。 
3演示3 
const p3  new Promise((resolved, rejected)  {setTimeout(()  {rejected();});
});console.log(p3, p3);
setTimeout(()  console.log(p3-setTimeout, p3)); //rejected在以上的这段代码中控制台打印结果如下。 在这段代码中 p3 一开始打印的是 pending 状态因为它没有执行到 setTimeout 里面。等到后续执行 setTimeout 时同样地会触发到 rejected 函数触发后返回一个 rejected 状态的 promise 。 
看完 promise 状态的变化后相信大家对 promise 的三种状态分别在什么时候触发会有一定的了解。那么我们接下来继续看 promise 状态的表现。 
2状态的表现 
pending 状态不会触发 then 和 catch 。fulfilled 状态会触发后续的 then 回调函数。rejected 状态会触发后续的 catch 回调函数。 
我们来演示一下。 
1演示1 
const p1  Promise.resolve(100); //fulfilled
console.log(p1, p1);
p1.then(data  {console.log(data, data);
}).catch(err  {console.error(err, err);
});在以上的这段代码中控制台打印结果如下。 在这段代码中 p1 调用 promise 中的 resolved 回调函数此时执行时 p1 属于 fulfilled 状态 fulfilled 状态下只会触发 .then 回调函数不会触发 .catch 所以最终打印出 data 100 。 
2演示2 
const p2  Promise.reject(404); //rejected
console.log(p2, p2);
p2.then(data  {console.log(data2, data);
}).catch(err  {console.log(err2, err);
})在以上的这段代码中控制台打印结果如下。 在这段代码中 p2 调用 promise 中的 reject 回调函数此时执行时 p1 属于 reject 状态 reject 状态下只会触发 .catch 回调函数不会触发 .then 所以最终打印出 err2 404 。 
对三种状态有了基础了解之后我们来深入了解 .then 和 .catch 对状态的影响。 
3、then和catch对状态的影响重要 
then 正常返回 fulfilled 里面有报错则返回 rejected catch 正常返回 fulfilled 里面有报错则返回 rejected 。 
我们先来看第一条规则 then 正常返回 fulfilled 里面有报错则返回 rejected 。 
1演示1 
const p1  Promise.resolve().then(()  {return 100;
})
console.log(p1, p1); //fulfilled状态会触发后续的.then回调
p1.then(()  {console.log(123);
});在以上的这段代码中控制台打印结果如下。 在这段代码中 p1 调用 promise 中的 resolve 回调函数此时执行时 p1 正常返回 fulfilled  不报错所以最终打印出 123 。 
2演示2 
const p2  Promise.resolve().then(()  {throw new Error(then error);
});
console.log(p2, p2); //rejected状态触发后续.catch回调
p2.then(()  {console.log(456);
}).catch(err  {console.error(err404, err);
});在以上的这段代码中控制台打印结果如下。 在这段代码中 p2 调用 promise 中的 resolve 回调函数此时执行时 p2 在执行过程中抛出了一个 Error 所以里面有报错则返回 rejected 状态  所以最终打印出 err404 Error: then error 的结果。 
我们再来看第二条规则 catch 正常返回 fulfilled 里面有报错则返回 rejected 。 
1演示1需特别谨慎! ! 
const p3  Promise.reject(my error).catch(err  {console.error(err);
});
console.log(p3, p3); //fulfilled状态注意触发后续.then回调
p3.then(()  {console.log(100);
});在以上的这段代码中控制台打印结果如下。 在这段代码中 p3 调用 promise 中的 rejected 回调函数此时执行时 p3 在执行过程中正常返回了一个 Error 这个点需要特别谨慎这看起来似乎有点违背常理但对于 promise 来说不管时调用 resolved 还是 rejected 只要是正常返回而没有抛出异常都是返回 fulfilled 状态。所以最终 p3 的状态是 fulfilled 状态且因为是 fulfilled 状态之后还可以继续调用 .then 函数。 
2演示2 
const p4  Promise.reject(my error).catch(err  {throw new Error(catch err);
});
console.log(p4, p4); //rejected状态触发.catch回调函数
p4.then(()  {console.log(200);
}).catch(()  {console.log(some err);
});在以上的这段代码中控制台打印结果如下。 在这段代码中 p4 依然调用 promise 中的 reject 回调函数此时执行时 p4 在执行过程中抛出了一个 Error 所以里面有报错则返回 rejected 状态  此时 p4 的状态为 rejected 之后触发后续的 .catch 回调函数。所以最终打印出 some err 的结果。 
4、then和catch的链式调用常考 
学习完以上知识后我们通过几道题来再总结回顾一下。 
第一题 
Promise.resolve().then(()  {console.log(1);
}).catch(()  {console.log(2);
}).then(()  {console.log(3);
});这道题打印的是 1 和 3 因为调用的是 promise 的 resolve 函数所以后续不会触发 .catch 函数。 
第二题 
Promise.resolve().then(()  {console.log(1);throw new Error(error);
}).catch(()  {console.log(2);
}).then(()  {console.log(3);
});这道题打印的是 1 和 2 虽然调用的是 promise 的 resolve 函数但是中间抛出了一个异常所以此时 promise 变为 rejected 状态所以后续不会触发 .then 函数。 
第三题 
Promise.resolve().then(()  {console.log(1);throw new Error(error);
}).catch(()  {console.log(2);
}).catch(()  {  //这里是catchconsole.log(3);
});这道题打印的是 1 和 2 和 3 跟第二题一样中间抛出了一个异常所以此时 promise 变为 rejected 状态所以后续只触发 .catch 函数。 
三、async/await 
现代 js 的异步开发基本上被 async 和 await 给承包和普及了。虽然说 promise 中的 .then 和 .catch 已经很简洁了但是 async 更简洁它可以通过写同步代码来执行异步的效果。如此神奇的 async 和 await 究竟是什么呢让我们一起来一探究竟吧 
1、引例阐述 
先用一个例子来展示 promise 和 async/await 的区别。假设我们现在要用异步来实现加载图片。 
1 如果用 promise 的 .then 和 .catch 实现时代码如下 
function loadImg(src){const picture  new Promise((resolve, reject)  {const img  document.createElement(img);img.onload  ()  {resolve(img);}img.onerror  ()  {const err  new Error(图片加载失败 ${src});reject(err);}img.src  src;})return picture;
}const url1  https://dss1.baidu.com/6ONXsjip0QIZ8tyhnq/it/u2144980717,2336175712fm58;
const url2  https://dss1.baidu.com/6ONXsjip0QIZ8tyhnq/it/u614367910,2483275034fm58;loadImg(url1).then(img1  {console.log(img1.width);return img1; //普通对象
}).then(img1  {console.log(img1.height);return loadImg(url2); //promise 实例
}).then(img2  {console.log(img2.width);return img2;
}).then(img2  {console.log(img2.height);
}).catch(ex  console.error(ex)); 
2 如果用 async 实现时代码如下 
function loadImg(src){const picture  new Promise((resolve, reject)  {const img  document.createElement(img);img.onload  ()  {resolve(img);}img.onerror  ()  {const err  new Error(图片加载失败 ${src});reject(err);}img.src  src;})return picture;
}const url1  https://dss1.baidu.com/6ONXsjip0QIZ8tyhnq/it/u2144980717,2336175712fm58;
const url2  https://dss2.bdstatic.com/8_V1bjqh_Q23odCf/pacific/1990552278.jpg;!(async function () {// img1const img1  await loadImg(url1);console.log(img1.width, img1.height);// img2const img2  await loadImg(url2);console.log(img2.width, img2.height);
})(); 
大家可以看到如果用第二种方式的话代码要优雅许多。且最关键的是通过 async 和 await 用同步代码就可以实现异步的功能。接下来我们开始来了解 async 和 await 。 
2、async和await 
背景解决异步回调问题防止深陷回调地狱 Callback hell Promise Promise then catch 是链式调用但也是基于回调函数async/await async/await 是同步语法彻底消灭回调函数是消灭异步回调的终极武器。 
3、async/await和promise的关系 
1 async/await 与 promise 并不互斥两者相辅相成。 
2 执行 async 函数返回的是 promise 对象。 
3 await 相当于 promise 的 then 。 
4 try…catch 可以捕获异常代替了 promise 的 catch 。 
接下来我们来一一(2)(3)(4)演示这几条规则。 
第一条规则 执行 async 函数返回的是 promise 对象。 
async function fn1(){return 100; //相当于return promise.resolve(100);
}const res1  fn1(); //执行async函数返回的是一个Promise对象
console.log(res1, res1); //promise对象
res1.then(data  {console.log(data, data); //100
});在以上的这段代码中控制台打印结果如下。 大家可以看到第一个 res1 返回的是一个 promise 对象且此时 promise 对象的状态是 fulfilled 状态所以可以调用后续的 .then 并且打印出 data 100 。 
第二条规则 await 相当于 promise 的 then 。 
!(async function (){const p1  Promise.resolve(300);const data  await p1; //await 相当于 promise的thenconsole.log(data, data); //data 300
})();!(async function () {const data1  await 400; //await Promise.resolve(400)console.log(data1, data1); //data1 400
})();在以上的这段代码中控制台打印结果如下。 大家可以看到 p1 调用 resolve 回调函数所以此时 p1 属于 fulfilled 状态之后 const data  await p1 中的await相当于 promise 的 then 又因为此时 p1 属于 fulfilled 状态所以可以对 .then 进行调用于是输出 data 300 。同理在第二段代码中 await 400 时 400 即表示 Promise.resolved(400) 因此属于 fulfilled 状态随后调用 .then 打印出 data1 400 结果。 
再来看一段代码 
!(async function (){const p2  Promise.reject(err1);const res  await p4; //await 相当于 promise的thenconsole.log(res, res); //不打印
})();在以上的这段代码中控制台打印结果如下。 大家可以看到 p2 调用 reject 回调函数所以此时 p2 属于 reject 状态。但因为await是触发 promise 中的 .then 所以此时 res 不会被触发于是后续不会对await进行操作控制台也就不对 console.log(res, res); 进行打印。 
第三条规则 try…catch 可以捕获异常代替了 promise 的 catch 。 
!(async function () {const p3  Promise.reject(err1); //rejected 状态try{const res  await p3;console.log(res);}catch(ex){console.error(ex); //try…catch 相当于 promise的catch}
})();在以上的这段代码中控制台打印结果如下。 大家可以看到 p3 调用 reject 回调函数所以此时 p3 属于 rejected 状态因此它不会执行 try 的内容而是去执行 catch 的内容 try…catch 中的 catch 就相当于 promise 中的 catch 且此时 p3 属于 rejected 状态因此执行 catch 浏览器捕获到异常报出错误。 
4、异步的本质 
从上面的分析中不管是 promise 还是 async/await 都是解决异步问题。但是呢异步的本质还是解决同步的问题所以异步的本质是 
async/await 是消灭异步回调的终极武器JS 是单线程的需要有异步需要基于 event loop async/await 是一个语法糖但是这颗糖非常好用 
我们来看两道 async/await 的顺序问题回顾 async/await 。 
第一题 
async function async1(){console.log(async start); // 2await async2();//await 的后面都可以看做是callback里面的内容即异步。//类似event loop//Promise.resolve().then(()  console.log(async1 end))console.log(async1 end); // 5
}async function async2(){console.log(async2); //3
}console.log(script start);  // 1
async1();
console.log(script end); // 4在以上的这段代码中控制台打印结果如下。 从上面这段代码中可以看到先执行同步代码 1 之后执行回调函数 async1() 在回调函数 async1() 当中先执行同步代码 2 之后遇到 await 值得注意的是 await 的后面都可以看作是 callback 里面的内容即异步内容所以先执行 await 中对应的 async2() 里面的内容之后把 await 后面所有的内容放置到异步当中。继续执行 4 等到 4 执行完时整个同步代码已经执行完最后再去执行异步的代码最终输出 5 的内容。 
同样的方式来来分析第二题。 
第二题 
async function async1(){console.log(async1 start); //2await async2();// 下面三行都是异步回调callback的内容console.log(async1 end); //5await async3();// 下面一行是回调的内容相当于异步回调里面再嵌套一个异步回调。console.log(async1 end 2); //7
}async function async2(){console.log(async2); //3
}async function async3(){console.log(async3); //6
}console.log(script start); //1
async1();
console.log(script end); //4在以上的这段代码中控制台打印结果如下。 这里就不再进行分析啦大家可以根据第一个案例的步骤进行分析。 
5、场景题 
最后的最后我们再来做两道题回顾我们刚刚讲过的 async/await 知识点。 
1async/await语法 
async function fn(){return 100;
}
(async function(){const a  fn(); //?? Promiseconst b  await fn(); //?? 100
})();(async function(){console.log(start);const a  await 100;console.log(a, a);const b  await Promise.resolve(200);console.log(b, b);const c  await Promise.reject(300); //出错了再往后都不会执行了console.log(c, c);console.log(end);
})(); //执行完毕打印出哪些内容
//start
//100
//2002async/await的顺序问题 
async function async1(){console.log(async1 start); // 2await async2();//await后面的都作为回调内容 —— 微任务console.log(async1 end); // 6
}async function async2(){console.log(async2); // 3
}console.log(script start); // 1setTimeout(function(){ //宏任务 —— setTimeoutconsole.log(setTimeout); // 8
}, 0);//遇到函数立马去执行函数
async1();//初始化promise时传入的函数会立刻被执行
new Promise(function(resolve){ //promise —— 微任务console.log(promise1); // 4resolve();
}).then(function(){ //微任务console.log(promise2); // 7
});console.log(script end); // 5//同步代码执行完毕event loop —— call stack 被清空
//执行微任务
//尝试触发 DOM 渲染
// 触发event loop执行宏任务 
这里就不再进行一一解析啦大家可以前面知识点的学习总计再回顾理解。 
四、写在最后 
关于 js 的异步问题以及异步的解决方案问题就讲到这里啦u1s1 promise 和 async/await 在我们日常的前端开发中还是蛮重要的基本上写异步代码时候都会用到 async/await 来解决。啃了16个小时总结了event loop 和 promise 、async/await 问题希望对大家有帮助。 
同时里面可能有一些讲的不好或者不容易理解的地方也欢迎评论区评论或私信我交流~ 关注公众号 星期一研究室 不定期分享学习干货~ 如果这篇文章对你有用记得点个赞加个关注哦~