深入理解ES6中Promise以及使用场景 介绍Promise 译为承诺是异步编程的一种解决方案比传统的解决方案回调函数更加合理和更加强大在以往我们如果处理多层异步操作我们往往会像下面那样编写我们的代码doSomething(function(result){doSomethingElse(result,function(newResult){doThirdThing(newResult,function(finalResult){console.log(得到最终结果: finalResult);},failureCallback);},failureCallback);},failureCallback);阅读上面代码是不是很难受上述形成了经典的回调地狱现在通过 Promise 的改写上面的代码doSomething().then(function(result){returndoSomethingElse(result);}).then(function(newResult){returndoThirdThing(newResult);}).then(function(finalResult){console.log(得到最终结果: finalResult);}).catch(failureCallback);瞬间感受到 promise 解决异步操作的优点链式操作减低了编码难度代码可读性明显增强下面我们正式来认识 promise状态pending 进行中fulfilled 已成功rejected 已失败特点对象的状态不受外界影响只有异步操作的结果可以决定当前是哪一种状态一旦状态改变从 pending 变为 fulfilled 和从 pending 变为 rejected 就不会再变任何时候都可以得到这个结果用法Promise 对象是一个构造函数用来生成 Promise 实例constpromisenewPromise(function(resolve,reject){});Promise 构造函数接受一个函数作为参数该函数的两个参数分别是 resolve 和 rejectresolve 函数的作用是将 Promise 对象的状态从“未完成”变为“成功”reject 函数的作用是将 Promise 对象的状态从“未完成”变为“失败”实例方法Promise 构建出来的实例存在以下方法then()catch()finally()then()then 是实例状态发生改变时的回调函数第一个参数是 resolved 状态的回调函数第二个参数是 rejected 状态的回调函数then 方法返回的是一个新的 Promise 实例也就是 promise 能链式书写的原因getJSON(/posts.json).then(function(json){returnjson.post;}).then(function(post){// ...});catchcatch() 方法是 .then(null, rejection) 或 .then(undefined, rejection) 的别名用于指定发生错误时的回调函数getJSON(/posts.json).then(function(posts){// ...}).catch(function(error){// 处理 getJSON 和 前一个回调函数运行时发生的错误console.log(发生错误,error);});Promise 对象的错误具有“冒泡”性质会一直向后传递直到被捕获为止getJSON(/post/1.json).then(function(post){returngetJSON(post.commentURL);}).then(function(comments){// some code}).catch(function(error){// 处理前面三个Promise产生的错误});一般来说使用 catch 方法代替 then() 第二个参数Promise 对象抛出的错误不会传递到外层代码即不会有任何反应constsomeAsyncThingfunction(){returnnewPromise(function(resolve,reject){// 下面一行会报错因为x没有声明resolve(x2);});};浏览器运行到这一行会打印出错误提示 ReferenceError: x is not defined 但是不会退出进程catch() 方法之中还能再抛出错误通过后面 catch 方法捕获到finally()finally() 方法用于指定不管 Promise 对象最后状态如何都会执行的操作promise.then(result{···}).catch(error{···}).finally((){···});构造函数方法Promise 构造函数存在以下方法:all()race()allSettled()resolve()reject()try()all()Promise.all() 方法用于将多个 Promise 实例包装成一个新的 Promise 实例constpPromise.all([p1,p2,p3]);接受一个数组迭代对象作为参数数组成员都应为 Promise 实例实例 p 的状态由 p1 、 p2 、 p3 决定分为两种只有 p1 、 p2 、 p3 的状态都变成 fulfilled p 的状态才会变成 fulfilled 此时 p1 、 p2 、 p3 的返回值组成一个数组传递给 p 的回调函数只要 p1 、 p2 、 p3 之中有一个被 rejected p 的状态就变成 rejected 此时第一个被 reject 的实例的返回值会传递给 p 的回调函数注意如果作为参数的 Promise 实例自己定义了 catch 方法那么它一旦被 rejected 并不会触发 Promise.all() 的 catch 方法constp1newPromise((resolve,reject){resolve(hello);}).then(resultresult).catch(ee);constp2newPromise((resolve,reject){thrownewError(报错了);}).then(resultresult).catch(ee);Promise.all([p1,p2]).then(resultconsole.log(result)).catch(econsole.log(e));// [hello, Error: 报错了]如果 p2 没有自己的 catch 方法就会调用 Promise.all() 的 catch 方法constp1newPromise((resolve,reject){resolve(hello);}).then(resultresult);constp2newPromise((resolve,reject){thrownewError(报错了);}).then(resultresult);Promise.all([p1,p2]).then(resultconsole.log(result)).catch(econsole.log(e));// Error: 报错了race()Promise.race() 方法同样是将多个 Promise 实例包装成一个新的 Promise 实例constpPromise.race([p1,p2,p3])只要 p1 、 p2 、 p3 之中有一个实例率先改变状态 p 的状态就跟着改变率先改变的 Promise 实例的返回值则传递给 p 的回调函数constpPromise.race([fetch(/resource-that-may-take-a-while),newPromise(function(resolve,reject){setTimeout(()reject(newError(request timeout)),5000)})]);p.then(console.log).catch(console.error);allSettled()Promise.allSettled() 方法接受一组 Promise 实例作为参数包装成一个新的 Promise 实例只有等到所有这些参数实例都返回结果不管是 fulfilled 还是 rejected 包装实例才会结束constpromises[fetch(/api-1),fetch(/api-2),fetch(/api-3),];awaitPromise.allSettled(promises);removeLoadingIndicator();resolve()将现有对象转为 Promise 对象Promise.resolve(foo)// 等价于newPromise(resolveresolve(foo))参数可以分成四种情况分别如下:参数是一个 Promise 实例 promise.resolve 将不做任何修改、原封不动地返回这个实例参数是一个 thenable 对象 promise.resolve 会将这个对象转为 Promise 对象然后就立即执行 thenable 对象的 then() 方法参数不是具有 then() 方法的对象或根本就不是对象 Promise.resolve() 会返回一个新的 Promise 对象状态为 resolved没有参数时直接返回一个 resolved 状态的 Promise 对象reject()Promise.reject(reason) 方法也会返回一个新的 Promise 实例该实例的状态为 rejectedconstpPromise.reject(出错了);// 等同于constpnewPromise((resolve,reject)reject(出错了))p.then(null,function(s){console.log(s)});// 出错了Promise.reject() 方法的参数会原封不动地变成后续方法的参数Promise.reject(出错了).catch(e{console.log(e出错了)})// true使用场景将图片的加载写成一个 Promise 一旦加载完成 Promise 的状态就发生变化constpreloadImagefunction(path){returnnewPromise(function(resolve,reject){constimagenewImage();image.onloadresolve;image.onerrorreject;image.srcpath;});}通过链式操作将多个渲染数据分别给个 then 让其各司其职。或当下个异步请求依赖上个请求结果的时候我们也能够通过链式操作友好解决问题// 各司其职getInfo().then(res{let{bannerList}res//渲染轮播图console.log(bannerList)returnres}).then(res{let{storeList}res//渲染店铺列表console.log(storeList)returnres}).then(res{let{categoryList}res console.log(categoryList)//渲染分类列表returnres})通过 all() 实现多个请求合并在一起汇总所有请求结果只需设置一个 loading 即可functioninitLoad(){// loading.show() //加载loadingPromise.all([getBannerList(),getStoreList(),getCategoryList()]).then(res{console.log(res)loading.hide()//关闭loading}).catch(err{console.log(err)loading.hide()//关闭loading})}//数据初始化initLoad()