赞
踩
用于实现JS的异步机制, 非常重要!!!
执行流程(无其他异步操作, 如定时器):
用一段代码理解一下:
var p = new Promise(function(resolve, reject){
console.log(1)
resolve(2)
resolve(3)
reject(4)
console.log(5)
})
console.log(6)
p.then(function(val){
console.log('then',val)
}).catch(function(val){
console.log('catch', val)
})
console.log(7)
then 2
.除了 then(onresolve).catch(onreject)
的写法, 也可以不写catch, 把两个函数都作为then的参数, 即 then(onresolve, onreject)
的写法, 不过前者更直观一些, 推荐.
带有定时器的Promise
前面的是一个非常简单的例子. 如果我们在Promise传入的函数中设置了一个定时器, 延迟一定时间后在改变Promise的状态, 这样的话下面的then/catch都只能读取到pending状态? 这样会发生什么情况呢? 如下代码:
var p = new Promise(function(resolve, reject){
console.log(1)
//把修改状态的语句写入定时器.
setTimeout(function(){
resolve(2)
},3000)
console.log(5)
})
console.log(6)
p.then(function(val){
console.log('then',val)
}).catch(function(val){
console.log('catch', val)
})
console.log(7)
直接说结论, 输出的结果同上. 不过最后一个结果会等待三秒才输出.
当我们调用then/catch的时候, 如果当前的状态为pending, 则将对应的函数压入一个队列, 当改变Promise状态时, 再逐个执行这些函数.
下面来两段一个大佬自己实现的模拟Promise源码, 很觉得很有助于理解Promise的实现原理和运行机制.
原博客链接: https://www.jianshu.com/p/43de678e918a
思想还是很简单的:
当我们在创建Promise传入的回调函数中, 如果遇到resolve/reject, 判断当前状态是否为pending, 不是pending则直接忽略.
如果是pending, 则修改当前状态为fulfilled/rejected.
看第二段代码, 当我们执行then函数的时候, 如果Promise的状态为pending, 把对应的执行函数压到队列里面. 如果为fulfilled/rejected, 则直接执行then参数里面的函数.
再回到第一段代码, 就是上面then执行时遇到pending的情况, 当pending转化为fulfilled/rejected的时候, 调用相应方法, 查询执行函数队列, 并按顺序执行队列里面的函数. 这样就比较完美的实现了异步机制.
对于下面的第二段代码, 有个地方没实现好, then方法应该是异步执行的, 这里当Promise已经是fulfilled时, 直接执行then传入的回调函数. 我觉得应该这样去实现: setTimeout(()=>{onFulfilled(_value)},0)
.
// 添加resovle时执行的函数 _resolve (val) { if (this._status !== PENDING) return // 依次执行成功队列中的函数,并清空队列 const run = () => { this._status = FULFILLED this._value = val let cb; while (cb = this._fulfilledQueues.shift()) { cb(val) } } // 为了支持同步的Promise,这里采用异步调用 setTimeout(() => run(), 0) } // 添加reject时执行的函数 _reject (err) { if (this._status !== PENDING) return // 依次执行失败队列中的函数,并清空队列 const run = () => { this._status = REJECTED this._value = err let cb; while (cb = this._rejectedQueues.shift()) { cb(err) } } // 为了支持同步的Promise,这里采用异步调用 setTimeout(run, 0) }
then的实现:
// 添加then方法 then (onFulfilled, onRejected) { const { _value, _status } = this switch (_status) { // 当状态为pending时,将then方法回调函数加入执行队列等待执行 case PENDING: this._fulfilledQueues.push(onFulfilled) this._rejectedQueues.push(onRejected) break // 当状态已经改变时,立即执行对应的回调函数 case FULFILLED: onFulfilled(_value) break case REJECTED: onRejected(_value) break } // 返回一个新的Promise对象 return new MyPromise((onFulfilledNext, onRejectedNext) => { }) }
但是then地用法不止如此, 有时候你可能会看到一连串的then. 在上面代码也看到, then返回的是另一个Promise对象, 所以then之后可以继续接then的.返回的Promise对象的状态和参数是怎么样的呢? 源码我就不继续举了, 来个例子了解一下就好了.
var p = new Promise(function(resolve, reject){ resolve(666) }) .then(function(val){ console.log('then',val) return 'hello' }).then(function(val){ console.log(val) return 'world' }).then(function(val){ console.log(val) return new Promise(function(resolve, reject){ setTimeout(function(){ reject('goodbye') },3000) }) }).catch(function(val){ console.log(val) return 'see you again' }).then(function(val){ console.log(val) }) console.log(999)
代码略长, 但是不难理解.
hello
字符串.这一连串的then并不会阻塞主函数的运行, 所以输出的结果为: 999, then 666, hello, world, goodbye, see you again
.
定义函数时可以在函数前添加asnyc关键字, 函数执行与普通函数一致, 只不过对返回值进行了Promise的封装. 返回的Promise状态为fullfilled. (类似下面的封装).
function bar(fn, self){
return function(...rest){
let result = fn.apply(self, rest)
return Promise.resolve(result)
}
}
async的做法非常好理解. 但是await做的事情就多一些了.
首先, await必须在async的函数内部使用. await可以看作是一个运算符, 右边必须是一个Promise对象, 由于async返回的一定是一个promise对象, 所以await后面常跟async的函数(有点套娃的感觉, 但是只要理解上面说的await的用法, 这个说法就很容易接受).
await会阻塞当前函数的执行, 直到await拿到结果, 才会继续执行后面的内容. 那await等的是什么呢? 等的是对应的Promise对象, 等到他状态变为resolved.
console.log(1)
async function foo(){
console.log(2)
let tmp = await new Promise(resolve=>{
setTimeout(function(){
resolve(3)
},2000)
})
console.log(tmp,4)
console.log(5)
return 6
}
console.log(foo())
console.log(7)
//输出结果为: 1 2 Promise<pending> 7 (3,4) 5
从结果可以看出, await部分之前与普通函数一样正常执行. 但是之后的部分直接作为一个Promise对象封装并返回. 另外, await运算后不是一个Promise对象, 而是resolve的传入. 上面的函数和这个这个写法是等价的.
async function foo(){
return new Promise(resolve=>{
setTimeout(()=>{
resolve(3)
},2000)
}).then(v=>{
console.log(v,4)
console.log(5)
return 6
})
}
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。