目录

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 的时候,看到了好多分析原理的文章,一行行源码推敲,而且有时候作者自己也不是很清晰,一笔带过···

我感觉只要理解了这个思想和原理,用的时候大部分时间不会出错,不用去追究源码,也许这就是 意会 吧!