什么是Promise它是一个ES6提出一个新语法，用来优化异步代码的写法。Promise的基本使用Promise是承诺的意思，es6中提出一个新api。用来优化异步代码。它的经典使用方式如下:var p1 = new Promise(function(resolve,reject){ //异步操作 resolve(obj1) 或者 reject(obj2) }); p1.then(function(rs){ // 如果p1的状态是resolved，则then中的函数 //会执行，且obj1的值会传给rs }).catch(function(rs){ // 如果p1的状态是reject，则catch中的函数 // 会执行，且obj2的值会传给rs }).finally(function(){ // 一定会执行的函数 })构造器// const obj = new Object() // const arr = new Array() const p1 = new Promise(function(resolve,reject){ // 执行异步代码 // 调用resolve,或者reject }); console.dir(p1)要点:● 构造器必须要给定一个参数，如果不给就是会报错。例如，new Promise() 报错的信息是： Promise resolver undefined is not a function● 构造器的实参是一个函数，这个函数的特殊之处在于它有两个形参(resolve,reject)，这两个形参也是函数。在格式上，也可以采用箭头函数来改写。例如：var p1 = new Promise((resolve,reject)=>{})。● 在函数体的内部， 一般会执行异步代码，然后根据情况来调用resolve()或者是reject() 。调用resolve或者是reject后会产生什么样的后果，在后面小节介绍。 当然了，再次强调一下resolve和reject只是形参名，可以改写成其它的。三种状态和值一个Promise对象的状态可能是如下三种之一：pending,resolved,rejected.初始态pendingpending。它的意思是 "待定的，将发生的"，相当于是一个初始状态。 创建Promise对象时，且没有调用resolve或者是reject方法，相当于是初始状态。这个初始状态会随着你调用resolve，或者是reject函数而切换到另一种状态。var p = new Promise((resolve,rejectok,err)=>{ console.info('发呆.....' )}) console.dir(p)成功态resolved也有叫fulfilled的。resolved。表示解决了，就是说这个承诺实现了。 要实现从pending到resolved的转变，需要在 创建Promise对象时，在函数体中调用了resolve方法(即第一个参数)。var p = new Promise((resolve,reject)=>{ resolved();}) console.dir(p)注意注意，上面的resolve和reject仅是形参名而已。失败态rejectedrejected。拒绝，失败。表示这个承诺没有做到，失败了。要实现从pending到rejected的转换，只需要在创建Promise对象时，调用reject函数。var p = new Promise((resolve,reject)=>{ reject()} ) console.dir(p)三种状态小结1.状态是可转化。 最初创建promise对象时，默认状态是pending，如果在函数体内部调用了第一个参数对应的函数，则状态变成了resolved；如果调用了第二个参数对应的函数，则状态变成了rejected pending ----- resolve() --> resolved; pending ----- reject() --> rejected; 2.状态转换是不可逆的。 一旦从pending ---> resolved（或者是rejected），就不可能再回到pending，也不能由resolved变成rejected。promise的值一个promise对象除了状态之外，还有promiseValue。在构造器中，这个值在调用resolve和reject方法时传入例如var p = new Promise( (resolve,reject) => { resolve(123); } ); // 此时，prommise对象p的状态是 resolved，值是123。 console.dir(p)var p = new Promise( (resolve,reject) => { reject(123); } ); // 此时，prommise对象p的状态是 rejected，值是123 console.dir(p)在构造器的函数体中，一旦状态发生了变化，就会进行then，或者是catch中去，同时把promiseValue传入对应的函数。具体来说:状态从pending变成resolved，进入then中，调用函数，并传入此时的promiseValue（就是调用resolve时传入的实参）状态从pending变成rejected，进入catch中，调用函数，并传入此时的promiseValue（就是调用reject时传入的实参）根据实际情况的需要，也可以不加上finally()。使用promise改造回调函数基本套路第一步：建立模板。这里的模板是指固定的套路：写一个空函数，在函数体中的创建一个promise对象，并返回function fnName(){ var p = new Promise((resolve,reject)=>{ }) return p; }当然， 如果使用箭头函数的话，还可以简化。第二步：把异步功能写入构造器中，根据实际来决定是否调用resolve,reject。function fnName(){ var p = new Promise((resolve,reject)=>{ // 这里写具体的代码，并在某个恰当的时机去调用resolve和reject函数。 }) return p; }第三步：调用函数。通过fnName().then().catch() 结构来调用这个函数then的格式及执行逻辑作用then方法的作用是为Promise对象添加状态改变时的回调函数then的格式它有两个参数，每个参数都是函数。 第二个参数是可选的。如下:// p 是一个promise对象 p.then(函数1[,函数2])它的两个参数都是函数。第一个参数是resolved状态的回调函数。当p的状态从pending变成了resolved，函数1会执行。第二个参数是rejected状态的回调函数。 当p的状态从pending变成了rejected，函数2会执行。其中第二个参数是可选的，如果只写一个参数的话就是如下:promise对象.then(函数1) 执行逻辑var p = new Promise((resolve,reject)=>{ // resolve(val1); reject(val2) }) p.then( okVal=>{ console.info("成功"); console.log(okValue); }, errVal=>{ console.info("失败"); console.log(errValue); } )它的两个参数都是函数，其执行逻辑是:如果promise对象的状态是resolved，则then()会执行第一个函数，并传入当前的PromiseValue（即上面的val1）;如果promise对象的状态是rejected，则then()会执行第二个函数，并传入当前的PromiseValue（即上面的val2）;特别地，如果promise对象的状态是rejected，且此时then方法并没有设置第二个参数，就会向外抛出一个错误，错误的提示大约是Uncaught (in promise)。示例var p = new Promise((resolve,reject)=>{ resolve(1)//主动调用resolve，并传入 }) // 此时，P的状态是resolved，且值promiseValue 是1. p.then((res)=>{ // 因为p的状态是resolved，所以自动执行then的第一个参数，并且把promisevalue传进来。 console.log("then,ok",res) })执行输出结果是：then,ok,1then的返回值then()方法的返回值也是一个promise对象，所以它支持链式写法。但是要注意的是它的返回值是一个新的promise对象，与调用then方法的并不是同一个对象示例var p1 = new Promise(()=>{}); var p2 = p1.then(function f_ok(){}, function f_err(){}); // p2也是一个promise对象。 console.log(p1 === p2); // false如上代码可以说明p1.then()的结果是一个与p1不同的promise对象。换句话说，then()会封装一个全新的promise对象p2。那既然 p2也是一个promise对象，那么，p2的状态（promiseStatus）和值(promiseValue)分别是什么？p2的状态及promiseValue如何确定？p2的状态及promiseValue按如下规则来确定● 如果p1的状态是pending,则p2的状态也是pending。● 如果p1的状态是resolved，then()会去执行f_ok，则p2的状态由f_ok的返回值决定。 ○ 如果f_ok返回值不是promise对象，则p2的状态是resolved，且p2的promiseValue就是f_ok函数的return值。 ○ 如果f_ok返回值是一个promise对象，则p2的状态及promiseValue以这个promise对象为准。 ○ 如果f_ok这个函数内部发生了错误（或者是用户主动抛出错误），则p2的状态是rejected，且p2的promiseValue就是这个错误对象。● 如果p1的状态是rejected，then()会去执行f_err，则p2的状态由f_err的返回值决定。 ○ 如果f_err返回值不是promise对象，则p2的状态是resolved，且p2的promiseValue就是f_err函数的return值。 ○ 如果f_err返回值是一个promise对象，则p2的状态及promiseValue以这个promise对象为准。 ○ 如果f_err这个函数内部发生了错误（或者是用户主动抛出错误），则p2的状态是rejected，且p2的promiseValue就是这个错误对象。示例练习var p1 = new Promise(()=>{}); var p2 = p1.then(function f_ok(){}, function f_err(){}); // p2也是一个promise对象。 console.dir(p1); // pending console.dir(p2); // pendingvar p1 = new Promise((resolve,reject)=>{ resolve()}); var p2 = p1.then(function f_ok(){ return 1 }, function f_err(){}); // p2也是一个promise对象。 console.dir(p1); // resolved, undefined console.dir(p2); // resolved, 1特殊地，如果f_ok()中并没有return语句，则相当于是 return undefinedcatch的格式及用法catch的格式及用法catch()的格式及用法Promise.prototype.catch 是 Promise.prototype.then(null, reject)的别名，用于指定当promise对象的状态从pending变成rejected的回调函数 。var p1 = new Promise((resolve,reject)=>{ reject('s') }); p1.catch(function(err){ console.log(err); }) // 与下面的代码等价 p1.then(null, function(err){ console.log(err); })单独使用catch没有什么意义，它一般与then一块使用。如下new Promise((resolve,reject)=>{ }).then(function(result){ // 如果promise对象的状态是resolved的，就会到这里来，并且result会接收promiseValue的值 }).catch(function(err){ // 如果promise对象的状态是rejected的，就会到这里来，并且err会接收promiseValue的值 }) // 上面的代码如何拆分来写的话，等价于： var p1 = new Promise((resolve,reject){ }); var p2 = p1.then(function(result){ }); var p3 = p2.catch(function(err){ }) catch的返回值 catch的返回值仍是一个promise对象，确定它的值的方式与then(null,(errVal)=>{ })的方式一致。promise的链式调用链式调用: $().attr().val()p.then().then().then()...示例function do1() { console.log("任务1"); } function do2() { console.log("任务2"); } function do3() { console.log("任务3"); } function do4() { console.log("任务4"); } var p = new Promise((resolve,reject)=>{ resolve()}) p.then(do1) .then(do2) .then(do3) .then(do4); 结果输出是:任务1，任务2，任务3，任务4分析var p1 = p.then(do1); var p2 = p1.then(do2) var p3 = p2.then(do3) var p4 = p3.then(do4)第一步：由于p的状态是resolved,所以p.then(do1)中，do1函数会执行。输出任务1。第二步：确定p1的状态。按前面关于then的部分的介绍，p1的状态由do1()来决定。因为do1并没有明确指定返回值，则返回值就是undefined. p1的状态就是resolved。第三步：由于p1的状态是resolved，所以p1.then(do2)会继续执行do2。输出任务2 ，且p2的状态由do2来决定。与第二步的分析相同，p2的状态仍是resolved。第四步：接下来看p3。由于p2的状态是resolved，所以p2.then(do3)会继续执行do3。输出任务2 ，且p3的状态由do3来定，仍是resolved。最后：p3.then(do4)。由于p3的状态是resolved，所以执行do4。输出任务4 。小小一道面试题代码填空，并完成指定功能。function sleep(time){ // 请写出你的代码 } sleep(2000).then(()=>{ console.log("后续操作") }) console.log(2);目标 是让sleep 的功能与setTimeout一样：就是等2000毫秒之后再执行后续操作。参考代码如下：function sleep(time){ return new Promise(function(resolve,reject){ // 异步操作，根据执行结果，决定是否调用 resolve,reject setTimeout(function(){ resolve() }, time) }) }async-await语法async,await 是es7中新增的语法，用来进一步改进异步代码的写法，是promise升级版！asyncasync函数返回一个 Promise 对象。async函数内部return语句返回的值是Promise 对象的值function f1 () { return 1 } async function f2 () { return 1 } async function f3 () {} const r1 = f1() const r2 = f2() const r3 = f3() console.log(r1) // 1 console.log(r2) // Promise, resolved(1) console.log(r3) // Promise, resolved(undefined) r2.then(res => { console.log(res) }) r3.then(res => { console.log(res) })await 命令await的外层函数必须有一个async.正常情况下，await命令后面是一个 Promise 对象，返回该promise的值。如果不是 Promise 对象，就直接返回对应的值。 async函数内部的执行流程在执行async函数(设名为asyncF)时，进入函数内部按序执行同步代码遇到await，跳出asyncF函数，继续执行后续代码。当await后的异步代码执行完成之后，接着执行asyncF中的后续代码。const p1 = new Promise((resolve, reject) => { setTimeout(()=>resolve(100), 3000) }) async function f () { console.log('开始执行f') var a = await p1 console.log('p1完成了') console.log(a) return 1 } console.log('主函数运行...') const res = f() console.log('f()完成，返回值是', res) res.then(res => { console.log(res) })执行结果是：主函数运行... 开始执行f f()完成，返回值是 Promise { <pending> } ----此处等了3s---- p1完成了 100 1事件循环 eventloop - 面试js运行在宿主环境中。宿主环境有： 浏览器， nodejs浏览器本身是一个复杂的系统，它要做的事情非常多，例如： 执行js代码，请求图片资源，解析css，渲染页面，响应鼠标的点击等等。在实现层面，浏览器内部会用不同的功能模块去完成不同的事情。这些不同的模块就体现为进程进一步把进程进行划分：主进程。用来协调控制其他子进程。GPU进程。用于3D绘制等。渲染进程。就是我们说的浏览器内核，负责具体页面的渲染，脚本执行，事件处理等。浏览器的每个tab页背后就有一个渲染进程。具体来说，一个渲染进程包括：主线程。统一调度其他线程GUI渲染线程。负责渲染页面，布局和绘制。与JS引擎互斥。JS引擎线程。负责处理解析和执行javascript脚本程序。与GUI渲染线程互斥的事件触发线程。用来控制事件循环（鼠标点击、setTimeout、ajax等）。当事件满足触发条件时，将事件放入到JS引擎所在的执行队列中。setInterval与setTimeout所在的线程。定时任务并不是由JS引擎计时的，是由定时触发线程来计时的。计时完毕后，通知事件触发线程异步http请求线程。浏览器有一个单独的线程用于处理AJAX请求，当请求完成时，若有回调函数，通知事件触发线程。io线程。用来接收其他进程的消息。每个渲染进程（一个tab页）都有一个主线程，并且主线程非常繁忙，既要处理 DOM，又要计算样式，还要处理布局，同时还需要处理 JavaScript 任务以及各种输入事件。要让这么多不同类型的任务在主线程中有条不紊地执行，这就需要一个系统来统筹调度这些任务，这个统筹调度系统就是消息队列和事件循环。eventLoop主线程上要做很多事情，例如：js代码执行，页面布局计算，渲染等主线程同一时刻只能做一件事，事情多了就要排队。所以主线程维护了任务队列。某个事件发生时，事件触发线程 就把对应的任务添加到主线程的任务队列中。主线程上的任务完成之后，就会从任务队列中取出任务来执行。任务是以事件及其回调的方式存在的。当事件（用户的点击，图片的成功加载）发生时，将其回调添加到任务队列；主线程上的任务完成之后，就会从任务队列中取出任务来执行，此过程不断重复从而形成一个循环，称为eventLoop宏任务和微任务为什么任务要分为同步任务和异步任务?● 同步任务：同步任务不需要进行等待可立即看到执行结果，比如console● 异步任务：异步任务需要等待一定的时候才能看到结果，比如setTimeout、网络请求异步任务，又可以细分为宏任务和微任务。下面列举目前学过的宏任务和微任务。console.log('1') new Promise((resolve, reject) => { resolve('2') }).then((res) => { console.log(res) }) setTimeout(() => { console.log('3') }) new Promise((resolve, reject) => { resolve('4') }).then((res) => { console.log(res) }) console.log('5') ● 先执行同步代码● 遇到宏任务，放入队列● 遇到微任务，放入微任务队列● 执行栈为空 ○ 将微任务入栈执行● 所有的微任务完成之后，取出宏任务队列来执行面试题分析1console.log(1) setTimeout(function() { console.log(2) }, 0) const p = new Promise((resolve, reject) => { resolve(1000) }) p.then(data => { console.log(data) }) console.log(3)结果:1,3,1000,22console.log(1) setTimeout(function() { console.log(2) new Promise(function(resolve) { console.log(3) resolve() }).then(function() { console.log(4) }) }) new Promise(function(resolve) { console.log(5) resolve() }).then(function() { console.log(6) }) setTimeout(function() { console.log(7) new Promise(function(resolve) { console.log(8) resolve() }).then(function() { console.log(9) }) }) console.log(10)结果:1,5,10,6,2,3,4,7,8,93 console.log(1) setTimeout(function() { console.log(2) }, 0) const p = new Promise((resolve, reject) => { console.log(3) resolve(1000) // 标记为成功 console.log(4) }) p.then(data => { console.log(data) }) console.log(5)结果:1,3,4,5,1000,24 new Promise((resolve, reject) => { resolve(1) new Promise((resolve, reject) => { resolve(2) }).then(data => { console.log(data) }) }).then(data => { console.log(data) }) console.log(3)结果:3，2，1