Promise
如今写了很多门语言之后越来越有感触,事件驱动的设计离不开各种异步调用。比如 Android 中主线程如今已经不允许发送网络请求等等,这时候都需要一些异步回调。而在前端也是如此,但是前端8成以上的工程师能做的原理性的东西很少。
首先 JsCore 改不了吧?水平有限!其次,CSS 的解析改不了吧?写各种 loader,各种插件已经很牛逼了,我感觉那些 webpack 相关的东西,至今我也不是很懂,只是会用。所以大部分人都关注在业务开发上。
而客户端的工程师还有很多其他的事情研究,比如热更新啊、性能优化啊等等。
我觉得没有好坏之分,但是前端在代码架构上或者一些设计上我觉得更优秀,客户端相比很落后。比如双向绑定,redux 等思想。再比如这个 promise,虽然客户端也有可以处理这种事情的东西,比如 RxJava、RxAndroid 等等,但是感觉没这么直接,RxJava 也要不停 subscribeOn、observeOn 等等,或者你说你写个 compose,但是还是那样,不直观不直接。
为什么需要Promise呢
事件驱动,不阻塞主线程的情况下,耗时操作只能异步回调回来。过多的嵌套的异步代码,就会产生所谓的回调地狱
// 常规 callback 方式
asyncCall('hello', () => {
asyncCall2('hello', () => {
asyncCall3('hello', () => {
asyncCall4('hello', cb)
})
})
})
所以急需一个救世英雄,让这段代码变的更通俗易懂,嵌套更少,Promise 横空出世。我觉得即使RxJava,写起来也没有下面那么直观,特别是当一些人乱使用操作符后,整个就变味了,感觉完全可以用 RxJava 封装个直接的 Promise,去掉过多的操作符。
// promise 方式
Promise
.all([promises])
.then((resultArray) => {
})
.catch((err) => {
})
舒服了!
怎么用
直观上来使用
.then 就是异步回调来的结果
.catch 就是一些异常信息的处理,直接结束流程。
依葫芦画瓢,若果已经能拿到 Promise 对象,比如现在很多 js 网络库,自动返回一个 Promise 对象。那么你就可以
Promise
.then((resp) => {
// 后续处理
let data = resp.data
})
.catch((err) => {
})
如果没有返回 Promise 对象,创造一个也要上
new Promise((resolve,reject) => {
if (ok) {
resolve()
} else {
reject()
}
})
之后的操作同上
Promise 也有一些操作符,应该是自带一些函数。all, race等,这些也都很直接,没有什么门槛和副作用函数。
一开始的疑问
-
既然 .then 之后返回的是一个 Promise 对象,无限 .then 会咋样呢?
如果没有异步调用,其实就相当于这些代码在主线程里写的一样 -
.then 里面 return 一个 自己(比如说发网络请求)的 Promise 会怎么样?
不会咋样,之后继续 .then,就是你 return 的那个异步操作的结果的回调 -
.then 里面 return 一个 自己(比如说发网络请求)的 Promise 且 .then 了会咋样?
不会咋样··· 之后就是你 .then 之后的回调
没什么玄奥的
所以,其实只要返回的是一个 promise 对象一切好说, 如果不是一个 Promise 对象就一定要构造一个
,因为 .then 的 function 中,如果你有异步操作,而且并不封装成 Promise 对象,那么他并不会等异步回调回来,直接走完同步代码,执行了下一个 .then 方法,就会和预想情况不一样。
所以核心就是,你究竟什么时候需要异步操作,异步的时候如果在 .then 中,那个方法不光一定返回的是 Promise 对象,且还要是同步的
。
不是很好形容,例子
const rp = require('request-promise');
function getTicket(callback) {
setTimeout(() => {
callback()
}, 2000);
}
function get () {
console.log('step 3')
getTicket(ticket => {
return rp('http://www.baidu.com').
then(_ => {
console.log('step 4')
})
})
}
function test() {
console.log('step 1')
rp('http://www.baidu.com')
.then(_ => {
console.log('step 2')
return get()
}).then(_ => {
console.log('step 5')
})
}
test();
上面是个 H5 很常见的情况,就是先去发送一个网络请求,然后请求成功后,发送后续请求,但是后续请求可能依赖端上的一些参数,这些参数需要通过 bridge 异步去拿,比如此处的 getTicket 我故意模拟了一下 2s 时间。然后拿到 ticket,发送后续请求。
所以理想情况,这个代码,返回应该是 step 1 -> 2 -> 3 -> 4 -> 5
实际情况是:step 1 -> 2 -> 3 -> 5 -> 4
why?
其实就是因为 get 方法,虽然 return 了个 Promise, 但是其实没啥用对吧,因为你异步返回个对象有啥意义呢?同步代码已经执行完了!这就是我上面想说的,要同步返回一个 Promise 的意思。
可能觉得这谁都明白啊,但是当你嵌套很多层之后,很容易就记不清楚了,所以一定要头脑清晰。
那么,如果想要能正常运行怎么办呢?此时就需要自己构造 Promise!区别仅仅是 get 函数
const rp = require('request-promise');
function getTicket(callback) {
setTimeout(() => {
callback()
}, 2000);
}
function get () {
return new Promise((resolve,reject) => {
console.log('step 3')
getTicket(ticket => {
return rp('http://www.baidu.com').
then(_ => {
console.log('step 4')
resolve()
})
})
})
}
function test() {
console.log('step 1')
rp('http://www.baidu.com')
.then(_ => {
console.log('step 2')
return get()
}).then(_ => {
console.log('step 5')
})
}
test();
总结
对于前端的知识,我觉得没必要过度的追寻原理,比如 Promise 这个原理,如果简单的看,就是封装 callback,观察者呗?其实具体的我也不清楚~~
但是如果去看它的源码的话,肯定很复杂,我觉得可以去看看人家代码怎么设计的,没必要一行行的去推敲,特别是我这个外行。当我想知道怎么用 Promise 的时候,看到了好多分析原理的文章,一行行源码推敲,而且有时候作者自己也不是很清晰,一笔带过···
我感觉只要理解了这个思想和原理,用的时候大部分时间不会出错,不用去追究源码,也许这就是 意会
吧!