├── TODO.md ├── .zhijia ├── config ├── map └── hash ├── .gitignore ├── 01-issues-of-async.md ├── CHANGELOG.md ├── book.json ├── code ├── 2-3-1-catch.js └── 2-3-2-catch.js ├── 01-3-growing.md ├── 02-promise-intro.md ├── 03-2-async-test.md ├── package.json ├── gulpfile.js ├── 03-1-async-function-vs-promise.md ├── SUMMARY.md ├── 04-4-vuex.md ├── 10-review.md ├── README.md ├── 04-2-xiaochengxu.md ├── 01-1-start.md ├── 04-3-other.md ├── 02-2-promise-test.md ├── 04-1-downgrade.md ├── 02-3-promise-error.md ├── 03-async-function.md ├── 01-2-issue.md ├── qa.md ├── 04-lets-do-it.md ├── 02-1-promise-basic.md └── 02-04-promise-advanced.md /TODO.md: -------------------------------------------------------------------------------- 1 | 待增补内容 2 | ======== 3 | 4 | 这里主要放日常看到,但暂时写不进去的内容。 5 | -------------------------------------------------------------------------------- /.zhijia/config: -------------------------------------------------------------------------------- 1 | {"anthId":"102426","author":{"id":"117186","username":"肉山"}} -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # IDE 2 | 3 | # repo 4 | node_modules 5 | 6 | # test 7 | test 8 | 9 | # build 10 | build -------------------------------------------------------------------------------- /01-issues-of-async.md: -------------------------------------------------------------------------------- 1 | 异步的问题 2 | ======== 3 | 4 | 之所以会出现这样那样的解决方案,我之所以写这样的文章介绍这些解决方案,肯定是异步本身有问题。 5 | 6 | 是的,异步就是那样让人难以割舍,又那样让人不易亲近。 -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | 更新记录 2 | ======== 3 | 4 | 方便大家日后跟进阅读。 5 | 6 | ## 2017-07-16 7 | 8 | * 增加在 Vuex 的 action 中返回 Promise 对象,实现 `dispatch` 时处理异步的内容。 -------------------------------------------------------------------------------- /book.json: -------------------------------------------------------------------------------- 1 | { 2 | "plugins": ["disqus", "github"], 3 | "pluginsConfig": { 4 | "disqus": { 5 | "shortName": "js-async-tutorial" 6 | }, 7 | "github": { 8 | "url": "https://github.com/meathill/javascript-async-tutorial" 9 | } 10 | } 11 | } -------------------------------------------------------------------------------- /code/2-3-1-catch.js: -------------------------------------------------------------------------------- 1 | new Promise( resolve => { 2 | setTimeout( () => { 3 | resolve(); 4 | }, 2000); 5 | throw new Error('bye'); 6 | }) 7 | .then( value => { 8 | console.log( value + ' world'); 9 | }) 10 | .catch( error => { 11 | console.log( 'Error: ', error.message); 12 | }); -------------------------------------------------------------------------------- /01-3-growing.md: -------------------------------------------------------------------------------- 1 | 异步的发展 2 | ======== 3 | 4 | 最初,在浏览器环境下,大家遭遇的异步导致的问题还不是很严重。因为那会儿以事件侦听为主,大部分处理函数不需要嵌套很多层,也就是 Ajax 批量加载资源的时候可能有些头大,平时不怎么能听到这方面的抱怨。(所以大家都跑去做模组解决方案了,并没有在这方面很上心。) 5 | 6 | 但是当 Node.js 问世之后,对异步的依赖一下子加剧了。 7 | 8 | 因为那个时候,后端语言无论是 PHP、Java、Python 都已经相当成熟,Node.js 想要在服务器端站稳脚跟,必须有独到之处。于是,异步运算带来的无阻塞高并发就成了 Node.js 的镇店之宝、主打功能。 9 | 10 | 然而写了才知道,虽然能无阻塞高并发,但是无数层嵌套的回调函数也使得代码维护与重构变得异常困难,抱怨之声四起,大家方开始更努力的探索解决方案。 11 | 12 | 最终,Promise/A+ 被摸索出来。 -------------------------------------------------------------------------------- /02-promise-intro.md: -------------------------------------------------------------------------------- 1 | Promise 方案 2 | ======== 3 | 4 | > 有没有一种方案,既能保留异步在无阻塞上的优势,又能让我们写代码写的更舒服呢? 5 | 6 | 社区经过长时间探索,最终总结出 Promise/A+ 方案,并且纳入 ES2015 规范,如今,大部分运行环境都已经原生支持它。 7 | 8 | [![Promise 的普及率](http://zhijia-10060660.file.myqcloud.com/article/images/20170624003412_474.jpg)](http://caniuse.com/#search=promise) 9 | _Promise 的支持情况_ 10 | 11 | 这套方案有以下好处: 12 | 13 | 1. 可以很好的解决回调嵌套问题 14 | 2. 代码阅读体验很好 15 | 3. 不需要新的语言元素 16 | 17 | 这套方案由众多开发者共同探索得来,不同类库的实现略有不同,但日后都会支持 ES2015 标准。本文也以其为准。 -------------------------------------------------------------------------------- /.zhijia/map: -------------------------------------------------------------------------------- 1 | {"README.md":"106207","01-issues-of-async.md":"106208","01-1-start.md":"106209","01-2-issue.md":"106210","01-3-growing.md":"106211","02-promise-intro.md":"106212","02-1-promise-basic.md":"106213","02-2-promise-test.md":"106214","02-04-promise-advanced.md":"106215","03-1-async-function-vs-promise.md":"106216","04-lets-do-it.md":"106217","04-1-downgrade.md":"106218","04-2-xiaochengxu.md":"106219","04-3-other.md":"106220","10-review.md":"106221","02-3-promise-error.md":"106222","03-async-function.md":"106223"} -------------------------------------------------------------------------------- /03-2-async-test.md: -------------------------------------------------------------------------------- 1 | Async 小测验 2 | ======== 3 | 4 | Async Function 同样会有一些令人困惑的地方,我收集了一些,放在这里方便大家学习。 5 | 6 | 1. 执行顺序 7 | 8 | > 来自 [Axel Rauschmayer](https://twitter.com/rauschma/status/1188927319685120001) 9 | 10 | 下面两行代码的执行顺序一致么?不一致的话,差别在哪里? 11 | 12 | ```js 13 | /* 1 */ [one, two] = [await fn(1), await fn(2)]; 14 | /* 2 */ [one, two] = await Promise.all([fn(1), fn(2)]); 15 | ``` 16 | 17 | **答案:** 18 | 19 | 不一致。(1) 是顺序执行,因为构建数组时,会先等待 `fn(1)`、`fn(2)` 执行完;(2) 是并发执行,`fn(2)` 会在 `fn(1)` 执行完但是没有返回结果时立即执行。 20 | 21 | -------- 22 | 23 | 总结 24 | -------- 25 | 26 | 其实说起来是 Async 问题,其实都是语法问题。 27 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "javascript-async-tutorial", 3 | "version": "0.1.0", 4 | "description": "a full tutorial for javascript async coding", 5 | "main": "index.js", 6 | "scripts": { 7 | }, 8 | "repository": { 9 | "type": "git", 10 | "url": "git+ssh://git@bitbucket.org/meathillbooks/javascript-async-tutorial.git" 11 | }, 12 | "keywords": [], 13 | "author": "Meathill (http://meathill.com/)", 14 | "license": "CC-NY-NC-4.0", 15 | "homepage": "https://bitbucket.org/meathillbooks/javascript-async-tutorial#readme", 16 | "devDependencies": {} 17 | } 18 | -------------------------------------------------------------------------------- /code/2-3-2-catch.js: -------------------------------------------------------------------------------- 1 | new Promise( resolve => { 2 | setTimeout( () => { 3 | throw new Error('bye'); 4 | }, 2000); 5 | }) 6 | .then( value => { 7 | console.log( value + ' world'); 8 | }) 9 | .catch( error => { 10 | console.log( 'It\'s an Error: ', error.message); 11 | }); 12 | 13 | // ./code/2-3-catch-error.js:3 14 | // throw new Error('bye'); 15 | // ^ 16 | 17 | // Error: bye 18 | // at Timeout.setTimeout [as _onTimeout] (/Users/meathill/Documents/Book/javascript-async-tutorial/code/2-3-catch-error.js:3:11) 19 | // at ontimeout (timers.js:488:11) 20 | // at tryOnTimeout (timers.js:323:5) 21 | // at Timer.listOnTimeout (timers.js:283:5) -------------------------------------------------------------------------------- /gulpfile.js: -------------------------------------------------------------------------------- 1 | const fs = require('fs'); 2 | const util = require('util'); 3 | const gulp = require('gulp'); 4 | const del = require('del'); 5 | const sequence = require('run-sequence'); 6 | const concat = require('gulp-concat'); 7 | 8 | const TO = 'build/'; 9 | const readFile = util.promisify(fs.readFile); 10 | 11 | gulp.task('clear', () => { 12 | del(TO); 13 | }); 14 | 15 | gulp.task('gitchat', async () => { 16 | let summary = await readFile('./SUMMARY.md'); 17 | let chapters = /\]\(([^)]+\.md)\)/g[Symbol.match](summary) 18 | .map(chapter => chapter.slice(2, -1)); 19 | return gulp.src(chapters) 20 | .pipe(concat('all.md', { 21 | newLine: '\n\r' 22 | })) 23 | .pipe(gulp.dest(TO + 'gitchat/')); 24 | }); 25 | 26 | gulp.task('default', callback => { 27 | sequence( 28 | 'clear', 29 | 'gitchat', 30 | callback 31 | ); 32 | }); -------------------------------------------------------------------------------- /03-1-async-function-vs-promise.md: -------------------------------------------------------------------------------- 1 | Async Function VS Promise 2 | ======= 3 | 4 | ## 异步函数的优势 5 | 6 | 异步函数对异步编程来说,有非常很大的提升。尤其在 `try/catch/throw` 方面,异步函数可以延续以前的标准写法,还能正常检索堆栈信息,优势非常大。 7 | 8 | ## Promise 的价值 9 | 10 | 不过就目前来看,Promise (暂时)并不会被完全取代。 11 | 12 | ### 异步函数依赖 Promise 13 | 14 | 异步函数返回的是一个 Promise 对象;`await` 关键字只能等待 Promise 对象完成操作。 15 | 16 | 所以 Promise 的知识都还有效,大家也必须学习 Promise,才能更好的使用异步函数。 17 | 18 | ### Promise 能更好的提供队列操作,并且在对象之间传递 19 | 20 | Promise 本身是一个对象,所以可以在代码中任意传递。这意味着我们可以在程序的任何位置使用和维护队列,非常方便。 21 | 22 | 比如我们做一些办公产品,用户可能先做了 A,然后又做了 B。A 和 B 之间存在很强的先后顺序,不能乱。这个时候,队列的价值就能充分提现出来。 23 | 24 | ### 异步函数的支持率还不够 25 | 26 | 从上一节最后的截图可以看到,虽然异步函数的支持率已经很高了,但是在 iOS 10.2、Android 4 等关键平台上,还没有原生实现。这就需要我们进行降级兼容。 27 | 28 | 然而异步函数的降级代码很难写,Babel 转译后至少要增加3000行,对于一些轻量级应用来说代价不小。如果应用本身不大,或者异步操作并不复杂,用 Promise 可能是更好的选择。 29 | 30 | ## 小结 31 | 32 | Promise 和异步函数之间不存在替代关系,根据需求和场景选择合适的技术,永远不会错。 -------------------------------------------------------------------------------- /SUMMARY.md: -------------------------------------------------------------------------------- 1 | # Summary 2 | 3 | * [前言](README.md) 4 | * [异步的问题](01-issues-of-async.md) 5 | * [异步的起源](01-1-start.md) 6 | * [异步的问题](01-2-issue.md) 7 | * [异步的发展](01-3-growing.md) 8 | * [Promise 方案](02-promise-intro.md) 9 | * [Promise 入门](02-1-promise-basic.md) 10 | * [Promise 小测验](02-2-promise-test.md) 11 | * [Promise 错误处理](02-3-promise-error.md) 12 | * [Promise 进阶](02-04-promise-advanced.md) 13 | * [Async Functions 方案](03-async-function.md) 14 | * [Async Functions VS Promise](03-1-async-function-vs-promise.md) 15 | * [Async 小测验](03-2-async-test.md) 16 | * [一起实战吧](04-lets-do-it.md) 17 | * [降级](04-1-downgrade.md) 18 | * [小程序](04-2-xiaochengxu.md) 19 | * [其它场景](04-3-other.md) 20 | * [Vuex](04-4-vuex.md) 21 | * [回顾](10-review.md) 22 | 23 | ## 其它 24 | 25 | * [反馈意见](https://github.com/meathill/javascript-async-tutorial/issues) 26 | * [更新记录](CHANGELOG.md) 27 | * [待增补内容](TODO.md) 28 | -------------------------------------------------------------------------------- /.zhijia/hash: -------------------------------------------------------------------------------- 1 | {"README.md":"5e36cae2c39983107176bd7844508af4","01-issues-of-async.md":"f0556afee315ae3703b355e4c17145f1","01-1-start.md":"832e971e63810ea25aaf6014e727ae50","01-2-issue.md":"839750f494dfd0382dbfe0fa36725e94","01-3-growing.md":"7316c02bb9fddfb50ea69abb662d10c7","02-promise-intro.md":"d66533b7b37468fb44b6a5c0cf76bb25","02-1-promise-basic.md":"666c5f3bcb68de3831f323756c33fd9d","02-2-promise-test.md":"454c6f32bc335cdc5be0ed1581f819b4","02-04-promise-advanced.md":"bf39fb41b9fde55d122eb810c41d8512","03-1-async-function-vs-promise.md":"c0ed44f1a4ecfe808350567f7b6cc0b4","04-lets-do-it.md":"be986ab915c48f2ec408a4bab8680d85","04-1-downgrade.md":"84b2a959f217b5915839c36057ca6dff","04-2-xiaochengxu.md":"4e77a290643ee1097126293022590da6","04-3-other.md":"6231f24b9b0ddd9a4993d535ff409582","10-review.md":"392e878c3d88d9a4be18b35a47f3c256","02-3-promise-error.md":"65a08dacc5b3436c014985889459d705","03-async-function.md":"0a3c5ba1c676df553f41ec95794ffe68"} -------------------------------------------------------------------------------- /04-4-vuex.md: -------------------------------------------------------------------------------- 1 | Vuex 2 | ======== 3 | 4 | Vue 从去年开始大热,越来越多同学借助其全家桶进行开发。其中,Vuex 负责状态管理,换言之,在复杂应用中,Vuex 通常作为全局的数据中心。 5 | 6 | > 如果您还不熟悉 Vuex,可以阅读它的[官方文档](https://vuex.vuejs.org/zh-cn/intro.html)。 7 | 8 | Vuex 要求开发者使用显式的方式修改数据。对立刻生效的修改,调用 `stat.commit(type, payload)` 提交;对需要异步数据交互之后才能生效的修改,通过 `stat.dispatch(type, payload)` 提交。 9 | 10 | 比如说,一个电商网站,有限量促销商品,库存很少,于是很容易发生用户下单时才发现被抢空的情况。这个时候,系统就需要帮助用户重新加载促销商品。同时,还要给出相应提示。换言之,我们不仅需要提交异步修改,还要知道异步修改是什么时候完成的。 11 | 12 | 一方面,可以通过监测特定属性,也就是借助 `vm.$watch` 来进行。不过这种方式很难区分前置条件,比如我们可以 `.$watch` 商品列表,但是商品列表有好几种原因会刷新,如果都写在一起,逻辑就很分裂。另一种方法,利用 `stat.dispatch` 会返回对应 `action` 函数的返回值的特性,可以直接返回代理异步操作的 Promise,这样我们就可以给出适当的提示了。 13 | 14 | ```javascript 15 | // buy.js 16 | api.checkProduct(productId) // 检查商品是否还在 17 | .then( response => { 18 | if (response.code === 0) { 19 | return api.checkout(); // 正常,结账 20 | } 21 | throw new Error(response.code); 22 | }) 23 | .catch( err => { // 没了 24 | if (err.message === NO_MORE_PRODUCT) { 25 | let popup = PopupManager.alert('您要购买的商品已售空,正为您查找其它的促销商品....'); 26 | this.$store.dispatch(ActionTypes.REFETCH_SALES) 27 | .then(response => { 28 | popup.msg = '已重新获取促销商品,请尽快选购'; 29 | popup.state = PopupManager.READY; 30 | }); 31 | } 32 | }); 33 | 34 | // action.js 35 | [ActionTypes.REFETCH_SALES] ({commit, state}) { 36 | state.isFetching = true; 37 | return api.fetch() // 要点:这里的 Promise 会返回给 dispatch 的地方 38 | .then(json => { 39 | commit(MutationTypes.RESET_PRODUCT_LIST, json); 40 | }); 41 | } 42 | ``` -------------------------------------------------------------------------------- /10-review.md: -------------------------------------------------------------------------------- 1 | 回顾 2 | ======== 3 | 4 | ## Pormise 5 | 6 | 相较于传统的回调模式,Promise 有着巨大的进步,值得我们学习和使用。 7 | 8 | ### 优势 9 | 10 | 1. 可以很好地解决异步回调不好写、不好读的问题 11 | 2. 可以使用队列,并且在对象之间传递 12 | 3. 不引入新语言元素,大部分浏览器已经原生支持,可以放心使用;个别不支持的,也有完善的解决方案 13 | 14 | ### 不足 15 | 16 | 1. 引入了不少新概念、新写法,学习成本不低 17 | 2. 也会有嵌套,可能看起来还很复杂 18 | 3. 没有真正解决 `return/try/catch` 的问题 19 | 20 | ## Async Functions 21 | 22 | 异步函数是 ES2017 里非常有价值的新特性,可以极大的改善异步开发的环境。 23 | 24 | 除了覆盖率,几乎找不出什么黑点。 25 | 26 | 目前大部分主流浏览器都已经实现对它的支持,除了 IE 和 iOS 10.2。我们可以针对这两个系列的浏览器里进行降级,不过请注意,因为 Async Functions 引入了新的语法元素,所以必须用 Babel 转译的方式来处理。 27 | 28 | ## 一些小 Tips 29 | 30 | 这是我犯过的一些错误,希望成为大家前车之鉴。 31 | 32 | * `.resolve()` `.reject()` 不会自动 `return`。 33 | * Promise 里必须 `.resolve()` `.reject()` `throw err` 才会改变状态,`.then()` 不需要。 34 | * `resolve()` 只会返回一个值,返回多个值请用数组或对象。 35 | 36 | ## 参考阅读 37 | 38 | * [MDN Promise](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise) 39 | * [MDN Promise 中文](https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Promise) 40 | * [阮一峰:ECMAScript 6 入门 - Promise 对象](http://es6.ruanyifeng.com/#docs/promise) 41 | * [[翻译] We have a problem with promises](http://fex.baidu.com/blog/2015/07/we-have-a-problem-with-promises/) 42 | * [MDN Generator](https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Generator) 43 | * [Async Function](https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Statements/async_function) 44 | * [await](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/await) 45 | * [让微信小程序支持 ES6 的 Promise 特性](https://haojen.github.io/2016/11/23/wechat-app-promise/) 46 | * [util.promisify() in Node.js v8](http://farzicoder.com/util-promisify-in-Node-js-v8/) 47 | * [util.promisify 官方文档](https://nodejs.org/dist/latest-v8.x/docs/api/util.html#util_util_promisify_original) -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 前言 2 | ======== 3 | 4 | 为解决异步函数的回调陷阱,开发社区不断摸索,终于折腾出 Promise/A+。它的优势非常显著: 5 | 6 | 1. 不增加新的语法,可以立刻适配几乎所有浏览器 7 | 2. 以队列的形式组织代码,易读好改 8 | 3. 捕获异常方案也基本可用 9 | 10 | 这套方案在迭代中逐步完善,最终被吸收进 ES2015。不仅如此,ES2017 中还增加了 Await/Async,可以用顺序的方式书写异步代码,甚至可以正常抛出捕获错误,维护同一个栈。可以说彻底解决了异步回调的问题。 11 | 12 | 现在大部分浏览器和 Node.js 都已原生支持 Promise,很多类库也开始返回 Promise 对象,更有各种降级适配策略。Node.js 7+ 则实装了 Await/Async。如果您现在还不会使用,那么我建议您尽快学习一下。本文我准备结合近期的开发经验,全面介绍现代化的 JavaScript 异步开发。 13 | 14 | ## 关于这本书 15 | 16 | 这本书最开始的内容源于我在 [SegmentFault](https://segmentfault.com/) 做的一场分享: [Promise 的 N 种用法](https://segmentfault.com/l/1500000008757392)。 17 | 18 | 后来去 GitChat 分享时,选题比较犯难,就跟运营妹子商量要不再做一次同样题目,这样我也省事儿。结果挂出来后乏人问津,只好再跟运营开会讨论,然后决定更名为[《JavaScript 异步开发攻略》](http://gitbook.cn/books/594541981b8e4b4036720fc9/index.html),并且增加非 Promise 的内容。改名后终于过了下限,然后我就动手写,于是便有了这本小书。 19 | 20 | 我写这本书时就打算根据环境发展随时更新内容,没想到 GitChat 设计成后期不允许修改文章,要改只能把内容发给运营人工修改。因为我写的时候为了分章节好维护好管理,用的是 Git + Gitbook,索性脆放到 Gitbook 上好了。名字也改成现在这个样子,增加一个“全”字。 21 | 22 | ## 目标读者要求 23 | 24 | 1. 前端水平:初级、中级 25 | 2. 了解 JavaScript 26 | 3. 最好有异步开发经历,希望写出更好的代码 27 | 28 | ## 名词及约定 29 | 30 | * ES6 = ES2015 31 | * ES7 = ES2016 + ES2017 32 | * 异步函数 = Async Functions = Await/Async 33 | 34 | 范例代码会使用 ES6 的语法,也会混用 ES6 Module 和 CommonJS,请大家不要见怪。我会在代码当中加注释,其中会有一些关键内容,请大家不要忽略。 35 | 36 | 本文中所有代码均以 Node.js 7.0 为基础。 37 | 38 | ## 作者介绍 39 | 40 | 大家好,我叫翟路佳,花名“肉山”,这个名字跟 Dota 没关系,从高中起伴随我到现在。 41 | 42 | 我热爱编程,喜欢学习,喜欢分享,从业十余年,投入的比较多,学习积累到的也比较多,对前端方方面面都有所了解,希望能与大家分享。 43 | 44 | 我兴趣爱好比较广泛,尤其喜欢旅游,欢迎大家相互交流。 45 | 46 | 你可以在这里找到我: 47 | 48 | * [博客](http://blog.meathill.com) 49 | * [微博](http://weibo.com/meathill) 50 | * [GitHub](https://github.com/meathill) 51 | 52 | ## 版权许可 53 | 54 | 本书采用[“保持署名—非商用”创意共享4.0许可证](https://creativecommons.org/licenses/by-nc/4.0/)。 55 | 56 | 只要保持原作者署名和非商用,您可以自由地阅读、分享、修改本书。 57 | 58 | ## 反馈 59 | 60 | 如果您对于文中的内容有任何疑问,请在评论或 [Issue](https://github.com/meathill/javascript-async-tutorial/issues) 中告诉我。亦可发邮件给我:meathill[at]gmail.com。谢谢。 -------------------------------------------------------------------------------- /04-2-xiaochengxu.md: -------------------------------------------------------------------------------- 1 | 在小程序中使用 Promise 2 | ======== 3 | 4 | 国内开发者尤其是前端,肯定不能避开“小程序”这个话题。 5 | 6 | 从[小程序 API 文档](https://mp.weixin.qq.com/debug/wxadoc/dev/api/)可以看出,大部分交互都要藉由异步回调来完成(我猜测这多半是跟原生应用交互导致的)。所以自然而然的,我也想用更好的方式去操作。 7 | 8 | 因为客户端的 WebView 中不支持原生 Promise,所以“微信 Web 开发工具”中也移除了对 Promise 的支持,需要我们自己处理。 9 | 10 | 好在正如之前所说,Promise 不需要引入新的语言元素,兼容性上佳,所以我们只要引用成熟的 Promise 类库就好。这里我选择 [Bluebird](http://bluebirdjs.com/)。 11 | 12 | ## 安装 13 | 14 | ```bash 15 | cd /path/to/my-xcx 16 | npm install bluebird --save-dev 17 | ``` 18 | 19 | 这样会把 Bluebird 安装在小程序目录的 `node_modules` 里。因为小程序并不支持从中加载,所以我们需要手工把 `node_modules/bluebird/browser/bluebird.js` 复制出来,放到小程序的 `utils` 目录内。 20 | 21 | ## 使用 22 | 23 | 使用时只需要 `import Promise from './utils/bluebird';`,就可以在开发环境中自由使用 `new Promise()` 了。这里拿 `wx.login` 作为例子: 24 | 25 | ```javascript 26 | import Promise from './utils/bluebird'; 27 | 28 | return new Promise(resolve => { 29 | wx.login({ 30 | success(result) { 31 | resolve(result); 32 | } 33 | }); 34 | }) 35 | .then(result => { 36 | console.log(result); 37 | }); 38 | ``` 39 | 40 | ## 注意事项 41 | 42 | 经过我的实际测试,在小程序里抛出错误并不会被 `.catch()` 捕获,而是直接进入全局错误处理。所以在小程序开发中,我们需要放弃前面说的,尽量抛出错误的做法,转用 `reject()`。 43 | 44 | ```javascript 45 | // 不要这样做! 46 | new Promise( resolve => { 47 | wx.checkSession({ 48 | success() { 49 | resolve(); 50 | }, 51 | fail() { 52 | throw new Error('Weixin session expired'); 53 | } 54 | }); 55 | }); 56 | 57 | // 推荐这样做 58 | new Promise( (resolve, reject) => { 59 | wx.checkSession({ 60 | success() { 61 | resolve(); 62 | }, 63 | fail() { 64 | reject('Weixin session expired'); 65 | } 66 | }); 67 | }); 68 | ``` 69 | 70 | ## Async Functions 71 | 72 | “微信 Web 开发者工具”里面集成了 Babel 转译工具,可以将 ES6 编译成 ES5,不过 Async Functions 就不支持了。此时我们可以选择自行编译,或者只使用 Promise。 73 | 74 | 自行编译时,请注意,小程序页面没有 `