Promise —— 解决回调地狱html
Promise链式调用html5
Promise异常处理node
Promise.prototype.catch()(推荐)ajax
Promise静态方法编程
类型转换 —— Promise.resolve()json
a => b => c => dsegmentfault
回调层数越深,那么回调的维护成本越高api
//异步加载函数 function loadScript (src, callback) { let script = document.createElement('script') script.src = src script.onload = () => { callback() } document.head.append(script) } function test () { console.log('test') } loadScript('./1.js', test) // 1 // test
若是有三个这样的方式回调数组
function loadScript (src, callback) { let script = document.createElement('script') script.src = src script.onload = () => { callback(src) } document.head.append(script) } function test (name) { console.log(name) } loadScript('./1.js', function (script) { console.log(script) loadScript('./2.js', function (script) { console.log(script) loadScript('./3.js', function (script) { console.log(script) //... }) }) }) // 1 // ./1.js // 2 // ./2.js // 3 // ./3.js
虽然回调函数是全部异步编程方案的根基。可是若是咱们直接使用传统回调方式去完成复杂的异步流程,就会没法避免大量的回调函数嵌套。致使回调地狱的问题。promise
为了不这个问题。CommonJS
社区提出了Promise
的规范,ES6
中称为语言规范。
Promise
是一个对象,用来表述一个异步任务执行以后是成功仍是失败。
new Promise( function(resolve, reject) {…} );
new Promise(fn)
返回一个Promise
对象在
fn
中指定异步等处理
- 处理结果正常的话,调用
resolve
(处理结果值)- 处理结果错误的话,调用
reject
(Error
对象)
Promise
内部是有状态的 (pending、fulfilled、rejected) ,Promise
对象根据状态来肯定执行哪一个方法。Promise
在实例化的时候状态是默认pending
的,
- 当异步操做是完成的,状态会被修改成
fulfilled
- 若是异步操做遇到异常,状态会被修改成
rejected
。不管修改成哪一种状态,以后都是不可改变的。
返回resolve
const promise = new Promise((resolve, reject) => { resolve(100) }) promise.then((value) => { console.log('resolved', value) // resolve 100 },(error) => { console.log('rejected', error) })
返回reject
const promise = new Promise((resolve, reject) => { reject(new Error('promise rejected')) }) promise.then((value) => { console.log('resolved', value) },(error) => { console.log('rejected', error) // rejected Error: promise rejected // at E:\professer\lagou\Promise\promise-example.js:4:10 // at new Promise (<anonymous>) })
即使promise
中没有任何的异步操做,then
方法的回调函数仍然会进入到事件队列中排队。
使用Promise
去封装一个ajax
的案例
function ajax (url) { return new Promise((resolve, rejects) => { // 建立一个XMLHttpRequest对象去发送一个请求 const xhr = new XMLHttpRequest() // 先设置一下xhr对象的请求方式是GET,请求的地址就是参数传递的url xhr.open('GET', url) // 设置返回的类型是json,是HTML5的新特性 // 咱们在请求以后拿到的是json对象,而不是字符串 xhr.responseType = 'json' // html5中提供的新事件,请求完成以后(readyState为4)才会执行 xhr.onload = () => { if(this.status === 200) { // 请求成功将请求结果返回 resolve(this.response) } else { // 请求失败,建立一个错误对象,返回错误文本 rejects(new Error(this.statusText)) } } // 开始执行异步请求 xhr.send() }) } ajax('/api/user.json').then((res) => { console.log(res) }, (error) => { console.log(error) })
本质上也是使用回调函数的方式去定义异步任务结束后所须要执行的任务。这里的回调函数是经过then
方法传递过去的
Promise
最多见的误区。要使用promise
的链式调用的方法尽量保证异步任务的扁平化。promise
对象then
方法,返回了全新的promise
对象。能够再继续调用then
方法,若是return
的不是promise
对象,而是一个值,那么这个值会做为resolve
的值传递,若是没有值,默认是undefined
then
方法就是在为上一个then
返回的Promise
注册回调then
方法中回调函数的返回值会做为后面then
方法回调的参数Promise
,那后面then
方法的回调会等待它的结束promise
对象就能够调用.then()
,是promise
原型对象上的方法
promise.then(onFulfilled,onRejected);
onFulfilled
参数对应resolve
,处理结果值,必选
onRejected
参数对应reject,Error
对象,可选
Promise
对象会在变为resolve
或者reject
的时候分别调用相应注册的回调函数。
- 当
handler
返回一个正常值的时候,这个值会传递给Promise
对象的onFulfilled
方法。- 定义的
handler
中产生异常的时候,这个值则会传递给Promise
对象的onRejected
方法。这两个参数都是两个函数类型,若是这两个参数是非函数或者被遗漏,就忽略掉这两个参数了,返回一个空的
promise
对象。
// 普通的写法会致使有不稳定输出 function loadScript (src) { //resolve, reject是能够改变Promise状态的,Promise的状态是不可逆的 return new Promise((resolve, reject) => { let script = document.createElement('script') script.src = src script.onload = () => resolve(src) //fulfilled,result script.onerror = (err) => reject(err) //rejected,error document.head.append(script) }) } loadScript('./1.js') .then(loadScript('./2.js')) .then(loadScript('./3.js')) //不稳定输出 // 1 // 2 // 3 ---------------------------------------------------------------------------- // 若是把加载2和3的放在1的then方法中 function loadScript (src) { //resolve, reject是能够改变Promise状态的,Promise的状态是不可逆的 return new Promise((resolve, reject) => { let script = document.createElement('script') script.src = src script.onload = () => resolve(src) //fulfilled,result script.onerror = (err) => reject(err) //rejected,error document.head.append(script) }) } loadScript('./1.js') .then(() => { loadScript('./2.js') }, (err) => { console.log(err) }).then( () => { loadScript('./3.js') }, (err) => { console.log(err) }) // 稳定输出 // 1 // 不稳定输出 // 2 // 3 // ---------------------------------------------- //可是若是中间有错误的时候,下面的3仍是会执行。 loadScript('./1.js') .then(() => { loadScript('./4.js') }, (err) => { console.log(err) }).then( () => { loadScript('./3.js') }, (err) => { console.log(err) }) // 1 // 报错 // 3 // 不符合题意,若是是报错以后,3不该该执行 // ------------------------------------------------------- loadScript('./1.js') .then(() => { return loadScript('./2.js') }, (err) => { console.log(err) }).then(() => { return loadScript('./3.js') }, (err) => { console.log(err) }) // 不加返回值,依旧是一个空的promise对象,没法用resolve, reject影响下一步.then()的执行 // 添加返回值以后就能够稳定输出 // 1 // 2 // 3
异常处理有如下几种方法:
catc
h是promise
原型链上的方法,用来捕获reject
抛出的一场,进行统一的错误处理,使用.catch
方法更为常见,由于更加符合链式调用。
p.catch(onRejected);
ajax('/api/user.json') .then(function onFulfilled(res) { console.log('onFulfilled', res) }).catch(function onRejected(error) { console.log('onRejected', error) }) // 至关于 ajax('/api/user.json') .then(function onFulfilled(res) { console.log('onFulfilled', res) }) .then(undefined, function onRejected(error) { console.log('onRejected', error) })
.catch()
是对上一个.then()
返回的promise
进行处理,不过第一个promise
的报错也顺延到了catch
中then
的第二个参数形式,只能捕获第一个promise
的报错,若是当前then
的resolve
函数处理中有报错是捕获不到的。因此.catch
是给整个promise
链条注册的一个失败回调。推荐使用!!!!
function loadScript (src) { //resolve, reject是能够改变Promise状态的,Promise的状态是不可逆的 return new Promise((resolve, reject) => { let script = document.createElement('script') script.src = src script.onload = () => resolve(src) //fulfilled,result script.onerror = (err) => reject(err) //rejected,error document.head.append(script) }) } loadScript('./1.js') .then(() => { return loadScript('./2.js') }).then(() => { return loadScript('./3.js') }) .catch(err => { console.log(err) }) // throw new Error 不要用这个方法,要用catch和reject,去改变promise的状态的方式
还能够在全局对象上注册一个unhandledrejection
事件,处理那些代码中没有被手动捕获的promise
异常,固然并不推荐使用。
更合理的是:在代码中明确捕获每个可能的异常,而不是丢给全局处理
// 浏览器 window.addEventListener('unhandledrejection', event => { const { reason, promise } = event console.log(reason, promise) //reason => Promise 失败缘由,通常是一个错误对象 //promise => 出现异常的Promise对象 event.preventDefault() }, false) // node process.on('unhandledRejection', (reason, promise) => { console.log(reason, promise) //reason => Promise 失败缘由,通常是一个错误对象 //promise => 出现异常的Promise对象 })
静态方法 Promise.resolve(value)
能够认为是 new Promise()
方法的快捷方式。
Promise.resolve(42) //等同于 new Promise(function (resolve) { resolve(42) })
若是接受的是一个promise
对象,那么这个对象会原样返回
const promise2 = Promise.resolve(promise) console.log(promise === promise2) // true
若是传入的是一个对象,且这个对象也有一个then
方法,传入成功和失败的回调,那么在后面执行的时候,也是能够按照promise
的then
来拿到。
(这个then方法,实现了一个thenable的接口,便可以被then的对象)
promise
库转化成promise
对象Promise.reslove({ then: function(onFulfilled, onRejected) { onFulfilled('foo') } }) .then(function (value) { console.log(value) // foo })
promise
对象返回function test (bool) { if (bool) { return new Promise((resolve,reject) => { resolve(30) }) } else { return Promise.resolve(42) } } test(1).then((value) => { console.log(value) })
Promise.reject(error)
是和 Promise.resolve(value)
相似的静态方法,是 new Promise()
方法的快捷方式。
建立一个必定是失败的promise
对象
Promise.reject(new Error('出错了')) //等同于 new Promise(function (resolve) { reject(new Error('出错了')) })
若是须要同时进行多个异步任务,使用promise
静态方法中的all方法,能够把多个promise
合并成一个promise
统一去管理。
Promise.all(promiseArray);
Promise.all
生成并返回一个新的Promise
对象,因此它可使用Promise
实例的全部方法。参数传递promise
数组中全部的Promise
对象都变为resolve
的时候,该方法才会返回, 新建立的Promise
则会使用这些promise
的值。- 参数是一个数组,元素能够是普通值,也能够是一个
promise
对象,输出顺序和执行顺序有关,- 该函数生成并返回一个新的
Promise
对象,因此它可使用Promise
实例的全部方法。参数传递promise
数组中全部的Promise
对象都变为resolve
的时候,该方法才会返回完成。只要有一个失败,就会走catch
。- 因为参数数组中的每一个元素都是由
Promise.resolve
包装(wrap
)的,因此Paomise.all
能够处理不一样类型的promose
对象。
var promise = Promise.all([ // ajax函数是一个异步函数并返回promise,不须要关心哪一个结果先回来,由于是都完成以后整合操做 ajax('/api/users.json'), ajax('/api/posts.json') ]) Promise.then(function(values) { console.log(values) //返回的是一个数组,每一个数组元素对应的是其promise的返回结果 }).catch(function(error) { console.log(error) // 只要有一个失败,那么就会总体失败走到catch里面 })
Promise.race(promiseArray);
和
all
同样会接收一个数组,元素能够是普通值也能够是promise
对象,和all
不一样的是,它只会等待第一个结束的任务 。
// 下面的例子若是request超过了500ms,那么就会报超时错诶,若是小于500ms,则正常返回。 const request = ajax('/api/posts.json') const timeout = new Promise((resovle, reject) => { setTimeout(() => reject(new Error('timeout')), 500) }) Promise.race([ request, timeout ]) .then(value => { console.log(value) }) .catch(error => { console.log(error) })
执行顺序 : 宏任务 => 微任务 => 宏任务
微任务 是promise
以后才加入进去的,目的是为了提升总体的响应能力
咱们目前绝大多数异步调用都是做为宏任务执行,promise
的回调 &MutationObserver
&node
中的process.nextTick
会做为微任务执行
下面的例子,当前宏任务当即执行,then
是微任务会延后执行,setTImeout
是异步的一个宏任务也会延后执行。当前宏任务执行完毕以后,微任务会先执行完毕以后下一个宏任务才会执行。
console.log('global start') setTimeout(() => { console.log('setTimeout') }, 0) Promise.resolve() .then(( => { console.log('promise') })) .then(( => { console.log('promise2') })) .then(( => { console.log('promise3') })) console.log('global end') // global start // global end // promise // promise2 // promise3 // setTimeout
具体的牵扯到eventLoop
的东西以后再进一步探讨。
说实话这个是最近比较复杂的一个笔记了,给本身点个赞,标个特殊标记。