共计 9130 个字符,预计需要花费 23 分钟才能阅读完成。
目录
- 说到 Promise 就不得不说道说道这 —— 回调天堂
-
Promise —— 解决回调天堂
- Promise 语法标准
- Promise 的状态
- Promise 根本用法
- Promise 初体验
- Promise 的实质
-
Promise 链式调用
- 常见误区
- 链式调用的了解
- Promise.prototype.then()
-
Promise 异样解决
- then 中回调的 onRejected 办法
-
Promise.prototype.catch()(举荐)
- .catch 模式和后面 then 外面的第二个参数的模式,两者异样捕捉的区别:
- 全局对象上的 unhandledrejection 事件
-
Promise 静态方法
-
类型转换 —— Promise.resolve()
- 应用场景
- Promise.reject()
- 数据聚合 —— Promise.all()
- 竞争 —— Promise.race()
-
- Promise 执行时序 —— 宏工作 vs 微工作
- 深度分析:手写一个 Promise 源码
- ES6-ES10 学习幅员
说到 Promise 就不得不说道说道这 —— 回调天堂
a => b => c => d
回调层数越深,那么回调的保护老本越高
// 异步加载函数
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
是一个对象,用来表述一个异步工作执行之后是胜利还是失败。
Promise 语法标准
new Promise(function(resolve, reject) {…} );
new Promise(fn)
返回一个Promise
对象在
fn
中指定异步等解决
- 处理结果失常的话,调用
resolve
(处理结果值)- 处理结果谬误的话,调用
reject
(Error
对象)
Promise 的状态
Promise
外部是有状态的 (pending、fulfilled、rejected),Promise
对象依据状态来确定执行哪个办法。Promise
在实例化的时候状态是默认pending
的,
- 当异步操作是实现的,状态会被批改为
fulfilled
- 如果异步操作遇到异样,状态会被批改为
rejected
。无论批改为哪种状态,之后都是不可扭转的。
Promise 根本用法
返回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 初体验
应用 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)
})
Promise 的实质
实质上也是应用回调函数的形式去定义异步工作完结后所须要执行的工作。这里的回调函数是通过 then
办法传递过来的
Promise 链式调用
常见误区
- 嵌套应用的形式是应用
Promise
最常见的误区。要应用promise
的链式调用的办法尽可能保障异步工作的扁平化。
链式调用的了解
promise
对象then
办法,返回了全新的promise
对象。能够再持续调用then
办法,如果return
的不是promise
对象,而是一个值,那么这个值会作为resolve
的值传递,如果没有值,默认是undefined
- 前面的
then
办法就是在为上一个then
返回的Promise
注册回调 - 后面
then
办法中回调函数的返回值会作为前面then
办法回调的参数 - 如果回调中返回的是
Promise
,那前面then
办法的回调会期待它的完结
Promise.prototype.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
Promise 异样解决
异样解决有以下几种办法:
then 中回调的 onRejected 办法
Promise.prototype.catch()(举荐)
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 外面的第二个参数的模式,两者异样捕捉的区别:
.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 事件
还能够在全局对象上注册一个 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 静态方法
类型转换 —— Promise.resolve()
静态方法 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()
Promise.reject(error)
是和 Promise.resolve(value)
相似的静态方法,是 new Promise()
办法的快捷方式。
创立一个肯定是失败的 promise
对象
Promise.reject(new Error('出错了'))
// 等同于
new Promise(function (resolve) {reject(new Error('出错了'))
})
数据聚合 —— Promise.all()
如果须要同时进行多个异步工作,应用 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()
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 执行时序 —— 宏工作 vs 微工作
执行程序:宏工作 => 微工作 => 宏工作
微工作 是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
的货色之后再进一步探讨。
深度分析:手写一个 Promise 源码
深度分析:手写一个 Promise 源码
ES6-ES10 学习幅员
说实话这个是最近比较复杂的一个笔记了,给本人点个赞,标个非凡标记。