💎一站式轻松地调用各大LLM模型接口,支持GPT4、智谱、星火、月之暗面及文生图 广告
[TOC] # 题1、js 中哪些操作是异步的? 答: AJAX(与服务器通信)、setTimeout、setInterval、setImmediate (node)、process.nextTick(node) 。 # 题2、事件循环是怎么回事? 答: 事件循环(EventLoop),JS 代码执行的顺序。 JS 把代码分为三部分:同步代码、宏任务代码、微任务代码。 执行顺序: 同步 -> 微任务 -> 宏任务。 微任务代码: node 中的 process.nextTick 等 宏任务代码: ajax、 setTimeout、 setInterval 等 JS 在执行代码时分为三种任务: * 同步代码 * 异步代码 * 宏任务(macrotask): script(整体代码), AJAX、事件回调(鼠标键盘事件)、 setTimeout, setInterval, setImmediate(node独有), I/O, UI rendering * 微任务(microtask) (优先级更高) process.nextTick(node独有), Promises.then, Object.observe(废弃), MutationObserver 执行顺序: 1. 先执行同步代码 2. microtask (微任务) 中的代码 3. 宏任务中的代码 ~~~ 代码演示:只能在 node 环境中执行: ​ // 同步代码 console.log(1); ​ // 宏任务代码 setTimeout(() => {    console.log(2); }, 0) ​ // 微任务代码(node环境专有) process.nextTick(() => {    console.log(3) }) ​ // 同步代码 console.log(4); ​ // 输出结果: 1432 ~~~ # 题3、什么是回调地狱?如何解决? 答:在写异步代码时都需要使用回调函数,当回调的层次多时,代码不容易编写,容易出错。 解决方法: 使用 ES6 中新出的 promise 以及 ES8(ES2017)中新出的 async...await 改成同步写法。 ES6 --> ES2015 ES7 --> ES2016 ES8 --> ES2017 ~~~ // 代码一、传统多层嵌套回调函数(回调地狱) setTimeout(() => {    console.log(1)    setTimeout(() => {        console.log(2)        setTimeout(() => {            console.log(3)            setTimeout(() => {                console.log(4)           }, 0)       }, 0)   }, 0) }, 0) ​ // 代码二、把所有的异步代码封装成 Promise 对象(ES6 的 Promise 写法) ​ // 无法是哪种方法:首先要先把异步代码封装成一个 Promise 对象: let p1 = function () {    return new Promise(() => {        // 异步代码        setTImeout(() => {            console.log(1)       }, 0)   }) } let p2 = function () {    return new Promise(() => {        // 异步代码        setTImeout(() => {            console.log(2)       }, 0)   }) } let p3 = function () {    return new Promise(() => {        // 异步代码        setTImeout(() => {            console.log(3)       }, 0)   }) } let p4 = function () {    return new Promise(() => {        // 异步代码        setTImeout(() => {            console.log(4)       }, 0)   }) } // 依次调用(避免回调地狱) p1().then(() => {    return p2() }).then(() => {    return p3() }).then(() => {    return p4() }) ​ // 代码三、async...await (ES8出的)(以同步的方式写代码) (async function () {    await p1()    await p2()    await p3()    await p4() })() ​ ~~~ # 题4、什么是 Promise ? 干什么用的? 答: ES6中新出的。用途: 1. 可以将一段 `异步的代码` 封装成一个 Promise 对象 2. 封装完之后就可以使用 ES8 中的 async ... await ... 来使用同步的方式写代码了,不需要再写回调函数 # 题5、(封装 Promise简易版)如何将异步代码封装成一个没有返回值的 Promise 对象? 答: 简易版本的封装流程(没有参数、没有返回值): 1. 要定义一个函数 2. 在这个函数中创建一个 Promise 对象并返回 return new Promise 3. 把异步的代码写在 Promise 中 // 代码演示:如何将异步代码封装成 Promise 对象 ~~~ // 异步代码 setTimeout(function () {    console.log('hello') }, 0) ​ // 把上面的异步代码封装成一个 Promise 对象 function hello() {    return new Promise(function () {        setTimeout(function () {            console.log('hello1')       }, 0)   }) } ~~~ // 代码演示:使用 Promise 对象 ~~~ // 方法一、ES6 中原始语法 hello().then(function(){    // 异步代码执行完之后的操作 }) ​ 方法二、ES8  async ... awiat 高级语法 async function doHello() {     await hello()    // 执行异步     // 后续操作 } doHello() ~~~ # 题6、封装带返回值的 Promise 对象(完整)? 答: ~~~ // 模拟:一段代码的代码,这段代码返回 100 setTimeout(function(){    // 返回 100 这个数据    return 100 }, 100) ​ // 封装带成功、失败返回值的 Promise 对象 function getData() {    // 参数一、resolve,函数,成功时调用它来设置返回值    // 参数二、reject,函数,失败时调用它业设置失败的返回值    return new Promise(function(resolve, reject){        // 以下异步代码执行完之后得到 100        setTimeout(function(){            // 模拟成功            let ok = true            // 判断是否成功            if(ok) {                // 成功时返回 100                resolve(100)           } else {                // 失败时返回错误信息                reject('用户名不正确!')                           }       }, 100)   }) } ​ // ES6 使用 getData().then(res=>{    console.log(res)   // 100 }).catch(err=>{    console.log(err)   // 用户名不正确! }) ​ // ES8 使用 async function doData() {    let data = await getData().catch(err=>{console.log(err)})    console.log(data)   // 100 } doData() ~~~