十行代码实现高仿Promise

本文为饥人谷造轮子系列,用实现一个高仿Promise

问题

假设我们有一个需求:1. 获取用户所在的城市;2. 根据城市获取天气;3. 根据天气获取出行建议。那我们的代码应该是这样的

getCity(url1, function(){
  getWeather(url2, function(weather){
    getSuggestion(url3, function(suggestion){
      console.log(suggestion)
    })
  })
})

这就是典型的异步 callback 『回调地狱』,代码层层嵌套可读性很差。关于异步的解决方式可参考这篇文章 Node.js异步漫谈 – 知乎专栏

使用 Promise 是解决上述问题的一种方式,这里我们不去讲如何去使用内置的 Promise,而是带大家手把手写一个 Promise。

思路

我们希望有一个工具,能让我们使用下面的的写法来实现上述功能

promise.then(getCity)
    .then(getWeather)
    .then(getSuggestion)

整理下思路:

  1. Tool 是一个对象
  2. Tool 有 then 这个方法
  3. 执行 then 方法返回的应该还是 Tool 对象
function Promise(){}
Promise.prototype.then = function(fn){
  //todo...
  return this
}
var promise = new Promise()

那如何实现异步操作序列执行呢?关键思路如下:

在 promise 对象内容维护一个数组,当执行 promise.then(getCity) .then(getWeather) .then(getSuggestion) 时把这几个函数依次放入数组中。注意此时这些函数并没有执行。

执行promise.resolve()时,会从数组中拿出一个函数去执行。函数执行的过程中在异步操作的结果到来后会再次自动调用 promise.resolve(),触发下一个函数的取出并执行,下一个函数结果到来后再次自动调用promise.resolve() ……,这样就实现了异步链式执行。和原子弹爆炸原理类似。

所以需要对原来的异步函数做一点小小的改动,在数据到来的地方,加一个promise.resolve,用于启动后续函数的执行

function getCity(){
  var xhr = new XMLHttpRequest()
  xhr.open(url, 'get', true)
  xhr.onload = function(){
    if (this.status == 200) {
      promise.resolve(xhr.responseText)  //注意这里的promise.resolve
    }
  }
  xhr.send()
}

现在我们就能实现一个简易的 Promise 了,这里我们先暂不考虑特殊情况:

function Promise(){
  this.callbacks = []
}
Promise.prototype.then = function(fn){
  this.callbacks.push(fn)  //调用 then 时把函数放入数组
  return this              //返回当前对象供链式调用
}
Promise.prototype.resolve = function(data){
  var fn = this.callbacks.shift()  //当调用resolve时拿出一个函数
  fn&&fn(data)                     //执行这个函数,并且把resolve的参数做参数
}


var promise = new Promise()

promise.then(getCity)
    .then(getWeather)
    .then(getSuggestion)

promise.resolve()  //启动

function getCity(){
  setTimeout(function(){
    promise.resolve('杭州')
  }, 1000)
}
function getWeather(city){
  setTimeout(function(){
    promise.resolve(city + ' 晴天')
  }, 1000)
}
function getSuggestion(weather){
  setTimeout(function(){
    console.log(weather + ' 天气不错,可携女友与狗出行')
  }, 1000)
}

当然,如果觉得promise.resolve 单独启动一次看起来不舒服,也可以这样执行

getCity()
  .then(getWeather)
  .then(getSuggestion)

function getCity(){
  setTimeout(function(){
    promise.resolve('杭州')
  }, 1000)
  return promise   //注意这里
}

作者:若愚

41 thoughts on “十行代码实现高仿Promise”

  1. Hi there would you mind stating which blog platform you’re working with? I’m looking to start my own blog soon but I’m having a difficult time deciding between BlogEngine/Wordpress/B2evolution and Drupal. The reason I ask is because your layout seems different then most blogs and I’m looking for something unique. P.S Apologies for getting off-topic but I had to ask!|

  2. You could certainly see your expertise in the work you write. The arena hopes for more passionate writers like you who aren’t afraid to mention how they believe. Always follow your heart.|

  3. Wonderful goods from you, man. I’ve understand your stuff previous to and you’re just too wonderful. I really like what you’ve acquired here, certainly like what you are stating and the way in which you say it. You make it entertaining and you still take care of to keep it smart. I cant wait to read far more from you. This is really a terrific web site.|

  4. Thanks for your marvelous posting! I genuinely enjoyed reading it, you may be a great author.I will ensure that I bookmark your blog and will often come back very soon. I want to encourage you to ultimately continue your great writing, have a nice holiday weekend!|

  5. I was wondering if you ever considered changing the structure of your site? Its very well written; I love what youve got to say. But maybe you could a little more in the way of content so people could connect with it better. Youve got an awful lot of text for only having 1 or 2 pictures. Maybe you could space it out better?|

  6. Hmm is anyone else experiencing problems with the images on this blog loading? I’m trying to determine if its a problem on my end or if it’s the blog. Any responses would be greatly appreciated.|

  7. Hello there! I know this is kind of off topic but I was wondering if you knew where I could find a captcha plugin for my comment form? I’m using the same blog platform as yours and I’m having difficulty finding one? Thanks a lot!|

  8. hey there and thank you for your information – I have certainly picked up anything new from right here. I did however expertise some technical points using this website, since I experienced to reload the site many times previous to I could get it to load correctly. I had been wondering if your web host is OK? Not that I’m complaining, but sluggish loading instances times will sometimes affect your placement in google and could damage your high quality score if advertising and marketing with Adwords. Anyway I am adding this RSS to my e-mail and could look out for a lot more of your respective exciting content. Ensure that you update this again soon.|

  9. I absolutely love your site.. Excellent colors & theme. Did you develop this website yourself? Please reply back as I’m attempting to create my own site and want to learn where you got this from or just what the theme is named. Kudos!|

  10. Hello there, just became aware of your blog through Google, and found that it’s really informative. I am gonna watch out for brussels. I will appreciate if you continue this in future. Many people will be benefited from your writing. Cheers!|

  11. Write more, thats all I have to say. Literally, it seems as though you relied on the video to make your point. You clearly know what youre talking about, why throw away your intelligence on just posting videos to your site when you could be giving us something informative to read?|

  12. It is in point of fact a great and helpful piece of info. I’m happy that you shared this useful info with us. Please keep us informed like this. Thank you for sharing.|

  13. Have you ever considered about including a little bit more than just your articles? I mean, what you say is fundamental and everything. But imagine if you added some great photos or video clips to give your posts more, “pop”! Your content is excellent but with pics and clips, this blog could definitely be one of the most beneficial in its field. Excellent blog!|

  14. I blog frequently and I truly appreciate your content. The article has truly peaked my interest. I am going to bookmark your website and keep checking for new information about once a week. I subscribed to your Feed as well.|

  15. Hey there would you mind letting me know which webhost you’re working with? I’ve loaded your blog in 3 different browsers and I must say this blog loads a lot faster then most. Can you suggest a good internet hosting provider at a fair price? Thanks a lot, I appreciate it!|

  16. Magnificent beat ! I would like to apprentice while you amend your site, how can i subscribe for a blog site? The account helped me a acceptable deal. I had been tiny bit acquainted of this your broadcast offered bright clear concept|

发表评论

电子邮件地址不会被公开。