资讯专栏INFORMATION COLUMN

promise-then 深入理解异步

caozhijian / 753人阅读

摘要:异步常用的几种方式今天着重来讲解一下和队列有何关联在这里我们需要先理解在应用的过程中需要注意的地方一定要,或者出去不要,做副作用函数。抛出同步异常谈到,这是让更加赞的一点。再强调一次,这个函数并不需要关心这个异常是同步还是异步返回的。

异步常用的几种方式

setTimeout/EventEmitter/Promise/generator/async-await 今天着重来讲解一下 promise

setTimeout和Promise队列有何关联
setTimeout(function(){console.log(4)},0); 
new Promise(function(resolve){ 
    console.log(1) 
    for( var i=0 ; i<10000 ; i++ ){ i==9999 && resolve() } 
    console.log(2) })
.then(function(){ console.log(5) });
 console.log(3);
在这里我们需要先理解We have a problem with promises
    doSomething().then(function () {
      return doSomethingElse();
    });
    
    doSomething().then(function () {
      doSomethingElse();
    });
    
    doSomething().then(doSomethingElse());
    
    doSomething().then(doSomethingElse);
promise 在应用的过程中需要注意的地方
1.new promise (resolve,reject){};一定要resolve,或者reject 出去不要,做副作用函数。
2.then 操作里面 接收 promise 对象,如果在promise里面是执行的纯函数,也可以返回一个 promise。resolve("常量")。避免promise 穿透
正确使用 promise 金字塔代码

新手错误 #1: promise版的金字塔问题
观察大家如何使用 PouchDB 这类大型的 promise 风格的API,我发现大量错误的 promise 使用形式。最常见的错误就是下面这个:

remotedb.allDocs({
  include_docs: true,
  attachments: true
}).then(function (result) {
  var docs = result.rows;
  docs.forEach(function(element) {
    localdb.put(element.doc).then(function(response) {
      alert("Pulled doc with id " + element.doc._id + " and added to local db.");
    }).catch(function (err) {
      if (err.status == 409) {
        localdb.get(element.doc._id).then(function (resp) {
          localdb.remove(resp._id, resp._rev).then(function (resp) {

// et cetera...
是的,实际上你可以像使用回调一样使用 promises,恩,就像用打磨机去削脚趾甲一样,你确实可以这么做。

并且如果你以为这样的错误只限于初学者,那么你会惊讶于我实际上是在黑莓官方开发者博客上看到上面的代码。老的回调风格的习惯难以消灭。(至开发者: 抱歉选了你的例子,但是你的例子将会有积极的教育意义)

正确的风格应该是这样:

remotedb.allDocs(...).then(function (resultOfAllDocs) {
  return localdb.put(...);
}).then(function (resultOfPut) {
  return localdb.get(...);
}).then(function (resultOfGet) {
  return localdb.put(...);
}).catch(function (err) {
  console.log(err);
});

这种写法被称为 composing promises ,是 promises 的强大能力之一。每一个函数只会在前一个 promise 被调用并且完成回调后调用,并且这个函数会被前一个 promise 的输出调用,稍后我们在这块做更多的讨论。

4.promise 中foreach 操作,转换成 promise.all()=>返回一个数组

5.在任何应用到了promise的过程中,一定要注意使用catch 将异常抛出来,不然会堵塞整个加载

使用副作用调用而非返回

下面的代码有什么问题?

somePromise().then(function () {
  someOtherPromise();
}).then(function () {
  // Gee, I hope someOtherPromise() has resolved!
  // Spoiler alert: it hasn"t.
});

好了,现在是时候讨论一下关于 promises 你所需要知道的一切。

认真的说,这是一个一旦你理解了它,就会避免所有我提及的错误的古怪的技巧。你准备好了么?

就如我前面所说,promises 的奇妙在于给予我们以前的 return 与 throw。但是在实践中这到底是怎么一回事呢?

每一个 promise 都会提供给你一个 then() 函数 (或是 catch(),实际上只是 then(null, ...) 的语法糖)。当我们在 then() 函数内部时:

somePromise().then(function () {
  // I"m inside a then() function!
});

我们可以做什么呢?有三种事情:

return 另一个 promise
return 一个同步的值 (或者 undefined)
throw 一个同步异常
就是这样。一旦你理解了这个技巧,你就理解了 promises。因此让我们逐个了解下。

返回另一个 promise
这是一个在 promise 文档中常见的使用模式,也就是我们在上文中提到的 “composing promises”:

getUserByName("nolan").then(function (user) {
  return getUserAccountById(user.id);
}).then(function (userAccount) {
  // I got a user account!
});

注意到我是 return 第二个 promise,这个 return 非常重要。如果我没有写 returngetUserAccountById() 就会成为一个副作用,并且下一个函数将会接收到 undefined 而非 userAccount
返回一个同步值 (或者 undefined)
返回 undefined 通常是错误的,但是返回一个同步值实际上是将同步代码包裹为 promise 风格代码的一种非常赞的手段。举例来说,我们对 users 信息有一个内存缓存。我们可以这样做:

getUserByName("nolan").then(function (user) {
  if (inMemoryCache[user.id]) {
    return inMemoryCache[user.id];    // returning a synchronous value!
  }
  return getUserAccountById(user.id); // returning a promise!
}).then(function (userAccount) {
  // I got a user account!
});

是不是很赞?第二个函数不需要关心 userAccount 是从同步方法还是异步方法中获取的,并且第一个函数可以非常自由的返回一个同步或者异步值。

不幸的是,有一个不便的现实是在 JavaScript 中无返回值函数在技术上是返回 undefined,这就意味着当你本意是返回某些值时,你很容易会不经意间引入副作用。

出于这个原因,我个人养成了在 then() 函数内部 永远返回或抛出 的习惯。我建议你也这样做。

抛出同步异常
谈到 throw,这是让 promises 更加赞的一点。比如我们希望在用户已经登出时,抛出一个同步异常。这会非常简单:

getUserByName("nolan").then(function (user) {
  if (user.isLoggedOut()) {
    throw new Error("user logged out!"); // throwing a synchronous error!
  }
  if (inMemoryCache[user.id]) {
    return inMemoryCache[user.id];       // returning a synchronous value!
  }
  return getUserAccountById(user.id);    // returning a promise!
}).then(function (userAccount) {
  // I got a user account!
}).catch(function (err) {
  // Boo, I got an error!
});

如果用户已经登出,我们的 catch() 会接收到一个同步异常,并且如果 后续的 promise 中出现异步异常,他也会接收到。再强调一次,这个函数并不需要关心这个异常是同步还是异步返回的。

这种特性非常有用,因此它能够在开发过程中帮助定位代码问题。举例来说,如果在 then() 函数内部中的任何地方,我们执行 JSON.parse(),如果 JSON 格式是错误的,那么它就会抛出一个异常。如果是使用回调风格,这个错误很可能就会被吃掉,但是使用 promises,我们可以轻易的在 catch() 函数中处理它了。

一定要注意我们在then 函数内部的时候

我们可以做什么呢?有三种事情:

return 另一个 promise
return 一个同步的值 (或者 undefined)
throw 一个同步异常

参考资料

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/107627.html

相关文章

  • ES6-7

    摘要:的翻译文档由的维护很多人说,阮老师已经有一本关于的书了入门,觉得看看这本书就足够了。前端的异步解决方案之和异步编程模式在前端开发过程中,显得越来越重要。为了让编程更美好,我们就需要引入来降低异步编程的复杂性。 JavaScript Promise 迷你书(中文版) 超详细介绍promise的gitbook,看完再不会promise...... 本书的目的是以目前还在制定中的ECMASc...

    mudiyouyou 评论0 收藏0
  • [译] 深入理解 Promise 五部曲:1. 异步问题

    摘要:当引擎开始执行一个函数比如回调函数时,它就会把这个函数执行完,也就是说只有执行完这段代码才会继续执行后面的代码。当条件允许时,回调函数就会被运行。现在,返回去执行注册的那个回调函数。 原文地址:http://blog.getify.com/promis... 在微博上看到有人分享LabJS作者写的关于Promise的博客,看了下觉得写得很好,分五个部分讲解了Promise的来龙去脉。从...

    CHENGKANG 评论0 收藏0
  • 深入理解js引擎的执行机制

    摘要:深入理解引擎的执行机制最近在反省,很多知识都是只会用,不理解底层的知识。在阅读之前,请先记住两点是单线程语言的是的执行机制。所以,是存在异步执行的,比如单线程是怎么实现异步的场景描述通过事件循环,所以说,理解了机制,也就理解了的执行机制啦。 深入理解js引擎的执行机制 最近在反省,很多知识都是只会用,不理解底层的知识。所以在开发过程中遇到一些奇怪的比较难解决的bug,在思考的时候就会收...

    feng409 评论0 收藏0
  • JavaScript 异步

    摘要:从最开始的到封装后的都在试图解决异步编程过程中的问题。为了让编程更美好,我们就需要引入来降低异步编程的复杂性。写一个符合规范并可配合使用的写一个符合规范并可配合使用的理解的工作原理采用回调函数来处理异步编程。 JavaScript怎么使用循环代替(异步)递归 问题描述 在开发过程中,遇到一个需求:在系统初始化时通过http获取一个第三方服务器端的列表,第三方服务器提供了一个接口,可通过...

    tuniutech 评论0 收藏0
  • 深入理解ES6笔记(十一)Promise与异步编程

    摘要:回调函数模式类似于事件模型,因为异步代码也会在后面的一个时间点才执行如果回调过多,会陷入回调地狱基础可以当做是一个占位符,表示异步操作的执行结果。函数可以返回一个,而不必订阅一个事件或者向函数传递一个回调函数。 主要知识点:Promise生命周期、Promise基本操作、Promise链、响应多个Promise以及集成PromiseshowImg(https://segmentfaul...

    RayKr 评论0 收藏0

发表评论

0条评论

caozhijian

|高级讲师

TA的文章

阅读更多
最新活动
阅读需要支付1元查看
<