多应用+插件架构,代码干净,二开方便,首家独创一键云编译技术,文档视频完善,免费商用码云13.8K 广告
[toc] # 异步编程 ## 哪些代码是异步代码? 异步代码有两类:`微任务代码` 和 `宏任务代码`。 - 微任务(microtask) process.nextTick(node独有), Promises.then, MutationObserver - 宏任务(macrotask): AJAX、 setTimeout, setInterval, setImmediate(node独有)、 事件回调(鼠标键盘事件等)、 I/O ## 代码的执行顺序 同步 -> 异步 ( 微任务 -> 宏任务) 示例代码、 ``` // 同步 1 console.log('a') // 宏任务 5 setTimeout(() => { console.log('b') }) // 同步 2 console.log('c') // 微任务 4 Promise.resolve().then(() => console.log('r')) // 同步 3 console.log('e') // 最终的输出结果:acerb ``` ## 如何将异步代码封装成 Promise 对象? 代码演示: ``` // 有一段异步代码 setTimeout(()=>{ console.log('hello') }) // 把上面的异步代码封装成 Promise 对象 // 1. new Promise 对象 const p = new Promise(function(resolve, reject){ // 2. 把异步代码放到里面 setTimeout(()=>{ console.log('hello') // 3. 在异步代码执行结束之后改变 Promise 的状态 if (成功) { // 如果成功就调用 resolve 方法设置为 fulfilled 状态 resolve(成功返回的数据) } else { // 如果失败就调用 reject 方法设置为 rejected 状态 reject(返回的错误信息) } }) }) ``` ## Promise 是干什么用的?为什么要用? 异步代码需要使用回调函数,当需要按顺调用多个异步代码时,就必须一层一层的嵌套回调函数,这样的代码比较麻烦比较不好维护,这种多层嵌套的回调函数我们称之为 `回调地狱`: ~~~ // 连续执行4段异步代码,需要一层一层“向内套” setTimeout(()=>{ console.log(1) setTimeout(()=>{ console.log(2) setTimeout(()=>{ console.log(3) setTimeout(()=>{ console.log(4) }) }) }) }) ~~~ 为了避免编写回调地狱的代码,ES6 中提供了 Promise 来解决这个问题:把异步代码封装成 Promise 对象之后,代码会变成 `扁平` 而不是 `层层向里套`: ~~~ // 1、把异步代码封装成 Promise 对象: let p1 = function() { return new Promise((resolve,reject)=>{ // 异步代码 setTImeout(()=>{ console.log(1) resolve() }) }) } let p2 = function() { return new Promise((resolve,reject)=>{ // 异步代码 setTImeout(()=>{ console.log(2) resolve() }) }) } let p3 = function() { return new Promise((resolve,reject)=>{ // 异步代码 setTImeout(()=>{ console.log(3) resolve() }) }) } let p4 = function() { return new Promise((resolve,reject)=>{ // 异步代码 setTImeout(()=>{ console.log(4) resolve() }) }) } // 2. 依次调用(避免回调地狱) p1().then(()=>{ return p2() }).then(()=>{ return p3() }).then(()={ return p4() }) ~~~ ## async ... await ... 是干什么用的?如何用? Promise 虽然可以解决回调地狱的问题,但是还需要使用 .then 、.catch ,还是比较麻烦,所以为了进一步优化异步代码,ES8 在 Promise 的基础上又添加了 async/await 语法,可以 `以同步的方式编写异步代码`。 示例代码、 ~~~ // 上面封装好的 Promise 对象,可以直接以下面的方式调用, // 下面的函数 p1 执行完之后 ,才执行 p2,然后p3,然后p4 // await 必须要用在 async function 中 // await 的返回值就是异步代码成功时的返回值 // async 的返回值又是一个新的 Promise 对象 const p = async function(){ await p1() await p2() await p3() await p4() } // p 还是一个 Promise 对象 p().then(()=>{console.log('完成'}) ~~~