Essentially, a promise is a returned object to which you attach callbacks, instead of passing callbacks into a function.
一直在用Promise,但一直感觉自己没有真正彻底弄明白Promise,总差那么一点。 最后发现原因很简单,是我脑内对 Promise 建立的模型错误了。于是写一篇短文。
误区
const a = new Promise(resolve => {
const random = Math.random() > 0.5 ? 1:0
setTimeout(() => resolve(random), 1000)
})
复制代码
这是一个一秒后会返回0或者1的promise。在浏览器的 console 里试验:
> a
< Promise {<resolved>: 1}
> a
< Promise {<resolved>: 1}
> a
< Promise {<resolved>: 1}
> a
< Promise {<resolved>: 1}
复制代码
直到地老天荒,返回的永远是1。(当然小伙伴们试验的话,也有50%可能会出现永远是0的情况,总之resolved的值不会变,谢谢回复指正)
错误的脑内模型
事实上,虽然知道 promise 是一个对象,但脑内并没有接受
> typeof a
< "object"
复制代码
在我脑内的模型,promise 是一个"函数",先定义它,然后再调用它来真正使用
const b = 定义promise
b.then( 使用promise )
复制代码
但一个Promise在定义的时候就执行了! 以第一例的promise来说,在定义了a之后
// 一秒以内
a = { status: "pending", value: undefined }
// 一秒之后
a = { status: "resolved", value: 1 }
// 这里用了简化的模型来说明,事实上并不能使用 a.status 或者 a.value 来取值
复制代码
所以虽然在定义时并不知道最后的value会是1还是0,然而一旦定义了value就已经确定下来了,不会再变化了。
为啥会有错误的脑内模型?
因为正常的使用场景,我们使用的并不是 Promise,而是return Promise的函数!
fetch(url).then( ... ) // fetch 是一个返回promise的函数
readFile(url).then( ... ) //readFile 也是一个返回promise的函数
复制代码
因为Promise在定义时就会执行,所以正常的使用方式确实是在使用时现场定义!于是就会使用函数来包裹。再以第一例来说,要让promise返回值在0和1之间随机变化,只要用函数就行了:
const createRandom = () => {
const random = Math.random() > 0.5 ? 1:0
return new Promise(resolve => setTimeout(resolve, 1000, random))
// 今天刚发现setTimeout可以接受第三个参数,简化写法
}
// 执行
createRandom().then(r => console.log(r)) // 0
createRandom().then(r => console.log(r)) // 1
createRandom().then(r => console.log(r)) // 0
createRandom().then(r => console.log(r)) // 0
createRandom().then(r => console.log(r)) // 1
复制代码
容易糊涂的地方
还有一个在学习时感觉糊涂的地方 就是各种名称,比如 resolve,reject,pending, fullfilled, respond, error 啥的,其实把定义和使用分开,一切还比较清晰
// 定义
const some = () => new Promise((resolve, reject) => {
... 异步操作
... resolve(返回值)
... reject("reject的原因")
})
// 使用
some.then(
response => {
// ... 处理成功返回的 response,也就是定义时 resolve 的值
},
error => {
// ... 处理 error, 也就是定义时 reject 的值
}
)
复制代码
常用的方法
写mock的时候一定会写一个 delay 函数
const delay = ms => new Promise(r => setTimeout(r, ms))
delay(1000).then( ... )
复制代码
Promise.resolve / Promise.reject
写mock时使用
const success = Promise.resolve(1) // 返回1的promise
const fail = Promise.reject("connection error") // rejected promise
复制代码
注意可以将一个promise 作为参数传入 Promise.resolve()
, 其结果会返回promise的执行结果,也就是说在不确定一个 value 是否是 promise 时,可以安全使用
Promise.resolve(value).then( ... )
复制代码
Promise.all / Promise.race
当你希望在多个fetch request全完成时执行操作,Promise 提供了方法
Promise.all([fetch1, fetch2, fetch3]).then( ... )
复制代码
有少数情况需要返回一组请求里第一个返回的(不论成功失败),可以使用
Promise.race([fetch1, fetch2, fetch3]).then( ... )
复制代码
Promise.prototype.finally
Promise的状态:
期初是pending
,异步完成后的状态有个方便的总称叫settled
,settled
有两种情况,成功的状态叫resolved
(以前也称fullfilled
),失败的叫rejected
。
如果不论成功还是失败都想执行一段代码(也就是在settled
状态就执行),可以使用.finally
somePromise.then( ... ).catch( ... ).finally( ... )
复制代码