JavaScript 异步开发全攻略

把之前写的《JavaScript 异步开发全攻略》更新了内容,然后放在 Gitbook 上,https://meathill.gitbooks.io/javascript-async-tutorial/content/ 欢迎阅读分享,因为我会不时维护,请关注,star。

之前在 GitChat 做过一次分享:《JavaScript 异步开发全攻略》。在我看来,原始内容可能不够完美,但通过后来的维护,可以把它打磨得越来越好。昨天在 SF 上回答了一个 Vuex 的 action 里使用 Promise 的问题,然后就想去补充一下这方面的内容。结果发现 GitChat 竟然不支持编辑文章,只能把内容发给运营人工修改。

索性把内容放到 Gitbook 上好了,反正 GitChat SEO 也不做,文章也自然移动到第二页了。试了试,没有被墙,很好。于是简单整理了一下,上传。

欢迎阅读,欢迎分享,因为我会不时更新新内容,请关注 star:

JavaScript 异步开发全攻略

第一场 GitChat 总结

在 GitChat 做了一次分享,总结一下他们家和 SF 的差异。简单来说,GC 的文章式共享方便检索,SF 的视频在交流效率上更占优势。另外,GC 的钱实时到帐,很舒服。

开始之前,先做广告吧。

GitChat 分享 《JavaScript 异步开发全攻略》

为解决异步函数的回调陷阱,开发社区不断摸索,终于折腾出 Promise/A+。它不增加新的语法,可以适配几乎所有浏览器;以队列的形式组织代码,易读好改;捕获异常方案也基本可用。这套方案在迭代中逐步完善,最终被吸收进 ES2015。不仅如此,ES2017 中还增加了 Await/Async,可以用顺序的方式书写异步代码,甚至可以正常抛出捕获错误,维护同一个栈。可以说彻底解决了异步回调的问题。 现在大部分浏览器和 Node.js 都已原生支持 Promise,很多类库也开始返回 Promise 对象,更有各种降级适配策略。Node.js 7+ 则实装了 Await/Async。如果您现在还不会使用,那么我建议您尽快学习一下。

下次直播分享 前端面试攻略:JavaScript 排序与搜索

从事前端开发的同学很多从页面仔入门,比如说我,自学比例很大,有些时候会无意中忽视一些基础,比如算法、数据结构。这些欠缺在某些时候就会显得很致命,比如说面试,或者处理大量数据的场景。所以希望这样的一场分享能够帮助大家夯实原本不太扎实的基础,将来的开发之路更加顺畅。

目前早鸟票发送中,7月13日前门票5折,19日前75折,开播当日恢复全价。

继续阅读“第一场 GitChat 总结”

【修正】Promise N种用法-异步回调的问题-findLargest 解析

做慕课视频的时候,仔细琢磨了一下,发现之前讲的还是有问题,所以重新录了一遍。

做慕课视频的时候,仔细琢磨了一下,发现之前讲的还是有问题,所以重新录了一遍。

继续阅读“【修正】Promise N种用法-异步回调的问题-findLargest 解析”

Node.js 8 中的 util.promisify

Node.js 8 内建了“Promise 化”功能,可以把之前回调模式的方法,很简单的封装成支持 Promise 的方法,只需要调用 util.promisify(原函数) 即可。对于非 Node.js 标准风格的函数,也提供自定义转换函数的功能。

Node.js 8 于上个月月底正式发布,带来了很多新特性。其中比较值得注意的,便有 util.promisify() 这个方法。

如果你已经很熟悉 Promise,请继续往下看。如果你还不熟悉 Promise,可以先跳过去看下下章:Promise 介绍

util.promisify()

虽然 Promise 已经普及,但是 Node.js 里仍然有大量依赖回调的异步函数,如果我们把每个函数都封装一遍,那真是齁麻烦齁麻烦的,比齁还麻烦。

所以 Node.js 8 就提供了 util.promisify() 这个方法,方便我们把原来的异步回调方法改成支持 Promise 的方法,接下来,想继续 .then().then().then() 搞队列,还是 await 就看实际需要了。

我们看下范例,让读取目录文件状态的 fs.stat 支持 Promise:

const util = require('util');
const fs = require('fs');

const stat = util.promisify(fs.stat);
stat('.')
  .then((stats) => {
    // Do something with `stats`
  })
  .catch((error) => {
    // Handle the error.
  });

怎么样,很简单吧?按照文档的说法,只要符合 Node.js 的回调风格,所有函数都可以这样转换。也就是说,只要满足下面两个条件,无论是不是原生方法,都可以:

  1. 最后一个参数是回调函数
  2. 回调函数的参数为 (err, result),前面是可能的错误,后面是正常的结果

结合 Await/Async 使用

同样是上面的例子,如果想要结合 Await/Async,可以这样使用:

const util = require('util');
const fs = require('fs');

const stat = util.promisify(fs.stat);
async function readStats(dir) {
  try {
    let stats = await stat(dir);
    // Do something with `stats`
  } catch (err) { // Handle the error.
    console.log(err);
  }
}
readStats('.');

自定义 Promise 化处理函数

那如果现有的使用回调的函数不符合这个风格,还能用 util.promisify() 么?答案也是肯定的。我们只要给函数增加一个属性 util.promisify.custom,指定一个函数作为 Promise 化处理函数,即可。请看下面的代码:

const util = require('util');

// 这就是要处理的使用回调的函数
function doSomething(foo, callback) { 
  // ...
}

// 给它增加一个方法,用来在 Promise 化时调用
doSomething[util.promisify.custom] = function(foo) { 
  // 自定义生成 Promise 的逻辑
  return getPromiseSomehow(); 
};

const promisified = util.promisify(doSomething);
console.log(promisified === doSomething[util.promisify.custom]);
// prints 'true'

如此一来,任何时候我们对目标函数 doSomething 进行 Promise 化处理,都会得到之前定义的函数。运行它,就会按照我们设计的特定逻辑返回 Promise 实例。

我们就可以升级以前所有的异步回调函数了。

继续阅读“Node.js 8 中的 util.promisify”

第三次直播总结,兼谈技术教学

前几天完成了第三场直播:《Web 永恒不变的主题:布局——Box,Flex,Grid》。这次人数比第二次又下降了,让我不禁有点气馁。不过,总结总结继续上吧。

前几天完成了第三场直播:《Web 永恒不变的主题:布局——Box,Flex,Grid》,这里总结一下。

坦率的说,第二场直播给我造成了一些错觉。因为第二场直播比第一场多了十个人,差不多1/3,让我以为自己取得了不小的进步,甚至上一次总结的时候还信心满满。然而,这一场观众数又给我干了回来,甚至还不如第一次。感觉尚未稳固的信心又失去了……

尤其是,第二场开播之前,有将近40个人加入我开辟的答疑群,这个人数几乎和购买课程的人数相等。但是第三场,前后只有不到10个人加了进来。我只能自我安慰,告诉自己,很多人之前已经加入了……

从准备的充分程度来说,第二场 Promise 的 N 种用法,应该是最充分的。但是从技术的实用角度来看,第三场 CSS 布局应该也不差。而且理论上,CSS布局更基础,来听的人应该更多才是——我只能认为,Promise 解决的异步回调问题,比 Grid 解决的复杂网格布局,更重要,更有价值,

希望下一场会好一点吧!

接下来谈两点想法。

干货

从选题开始,我就不想讲什么个人发展,或者前端技术展望之类的。我不是说这个主题不好,我只是觉得这个对听众来说用处不大。如果听众是一个自我驱动非常强、喜欢技术、喜欢开发的人,我相信他应该很容易找到类似的信息,清楚自己的前进方向;相反,如果用户还比较迷茫,只是试探性的踏入这个领域,听了意义不大。

因为,之后真正的学习,会耗费大量的时间,这个时候就不是听听故事便足够了,而是需要个人反复练习。另一方面,每个人的情况不一样,想要制定合适自己的学习计划,恐怕只有一对一的交流,才能产生真正的价值。

总而言之,我决定只讲干货,也就是用户听了,他就会了,按照我讲的思路,12345,上手就能干活。我认为如此一来,多学多练,构建起自己的技术体系,比听别人的故事收益更大。至少,我当年就是这么学的。

但是就结果来看,我的课卖的并不好。我觉得跟我自身品牌有很大关系,还是要继续积累。上次课讲完,有个同学找到我,说觉得我的课更干货,鼓励我坚持下去。其实这个时候听到这样子的鼓励,心里还是挺暖的。

道 & 术

上次直播到问答时间,立刻就有同学提问一个我认为我已经说清楚的问题,或者说,我PPT里明确讲到的问题。对此我也进行了反思。

我当然不会认为是同学没有认真听讲,大家都是花钱来的。反省之后,我认为,在讲述的过程当中,我想传达的是“道”,也就是所以然;但是很多同学,因为经验问题,因为视野问题,他可能只想听“术”,也就是然。

从备课的角度来说,我一定要做到逻辑自洽,不仅要讲明其然,还要讲明其所以然。所以在实践过程中,我可能更关注的是讲明其所以然,也就是原理,回归到规范、定义、浏览器实现等更基础的地方;但是听众想知道的很可能是具体操作。比如布局,我会讲,外层元素没有被浮动的子元素撑开,是因为外层元素没有触发独立的 BFC,然后 BFC 是 XXOO,所以你这个时候要想办法触发它的 BFC;但是听众其实更关心“高度塌陷”怎么解决?浮动怎么清?BFC 什么的一下听不懂就整体忽略掉了。

这是我日后需要注意的地方:减少掉书袋,讲好What How Why。


最后给下一场打个广告。

写 CSS 也要开脑洞:万能的 :checked + label 将于4月27日,下周四,晚上8点直播。这场课程,是既有基础,又有进阶的中级课程。相信会对大家有所启发,欢迎大家光临。

第二次直播课程完结,兼谈对前端,对培训行业的看法

第二次直播结束,现场感觉还不错。继续推荐给大家:https://segmentfault.com/l/1500000008757392。顺便推荐新直播:https://segmentfault.com/l/1500000008879826。关于编程培训,我有几个观点:1. 未来前端变化不会那么大,这个时候做培训比较合适;2. 未来是终身学习的时代,程序员尤甚,我自身条件也合适干这个;3. 所以我准备今年好好干一干。

这篇文章写于上周,结果赶上服务器故障,写到一半去迁机器了……参见上篇文章

前些天播出了直播课程的第二讲,《Promise 的 N 种用法》,感觉播出本身还算成功。现场气氛不错,学员们积极提问,我做完解答,大家也纷纷表示听懂了,有收获。

如果人多点就更好了……

这里再打次广告,课程地址:https://segmentfault.com/l/1500000008757392。购买后可以任意回看,页面上还有推广链接,分享可以得实惠。如果大家想学 Promise 的用法,看这个视频,就够了,完全够了。

最开始策划这堂课的时候,把问题想简单了,结果做幻灯片的时候才发现,那句话:老师要想给学生一杯水,自己至少要有一桶水,古人诚不我欺。准备的时候花费了比第一次直播,比想象多得多的时间,讲试了两次,才得到了比较满意的结果。

这次购买的人数也比上次稍微多了几个,感谢亲友团支援,也希望更多的是真实用户吧!试讲的时候,在斗鱼也取得了不错的效果,增加了几个关注,还有人给了100鱼丸。以后录像的时候,都会选在斗鱼同步直播。如果能借机积累点人气的话就更好了。

总而言之,有进步。


做了两次直播之后,会有朋友问我说以后是不是就准备干这个了?关于这点我有几个想法跟大家分享。

选择这个时间去做前端培训,很合适

在过去很长一段时间里,前端这个行业,发展非常快。新技术新框架,层出不穷,快速更迭。对于我们这些从业者来说,确实不敢放松,甚至有些同学表示担心,学不过来。这个阶段我认为不能当导师,很可能误人子弟,既坑自己,又坑别人。

但是现在,情况有所改变。ES2017 增加的新特性数量,相对于 ES2015 而言,少得多。现在被大家津津乐道翘首以盼的,也就 Await/Async。CSS4 新增的特性,比 CSS3,也少很多。至于 ES2018,就更少了,写成博客都不带翻页的。这其实也很自然,就像 iPhone567,代际之间几乎没有什么大变化。因为过去的技术积累,其它语言当中有价值的特性,多半已经被前端吸收借鉴进来了。能够极大改善我们开发环境、生产环境的点,基本都已经被发掘过了。

未来的日子里,我们必须学会不断打磨手头已有的工具和技术,把思路从学习吸收,向深耕细作转换。比如用 ESlint 工具维护统一的代码风格,提升代码质量,提升安全系数,补足团队短板。或者,开拓前端的新边疆,比如 Hybrid,PWA,Electron 正在做的。

在这样的时刻,我觉得,可以停下来一段时间去做培训。正如我在上面所说,老师要给学生一杯水,自己至少要有一桶水。培训教学是非常好的总结、查缺补漏的手段。可以说利人利己。

换个角度,现在停下脚步做培训,一时半会儿也不会落后于行业。

终身学习的庞大市场

这是我的第一个想法,第二个想法从罗辑思维听来,便是所谓的“终身学习”。

在他的设想中,我们的未来,会是一个终身学习的时代,所有人都学习,并且是终身不断学习。我觉得至少在程序开发领域,他没说错,我自己就是例子。我2006年参加工作,做页面仔,切图。一边切一边学习 PHP,学习 JS,学习 AS,学习面向对象,学习开发模式,一路边学边干,走到现在。

我没有放松过学习,并且积极走出舒适领域,主动逼自己学习。回想我的学习历程,主要分两部分:

  1. 刚工作的时候,跟前辈学,靠脸皮厚,抱着大腿猛学
  2. 把他们学得差不多了,就只能自学,订阅博客,关注微博,以及看文档读源码

然而我发现,并不是所有人都能自学。比如前阵子认识个小妹,我发给她文档链接,她说看不懂英文……然而她必须学会,因为他们公司只有她一个前端。所以这又是一个非常大的市场,即由我,藉用我磁性的声音,清晰的普通话,带领他们学习,帮助他们学习。

并且现阶段来看,用户的付费意愿,也蛮强烈;付费习惯,也基本养成。

而且我觉得我周围的大多数人,还不太认可这一点,那么这也是一个机会。培训讲师常有,有10+年一线开发经验的资深前端工程师,愿意做培训讲师的,则不常有。

与时间赛跑

综合前面两点,我觉得,眼下这个时机,很适合切换到新跑道。毕竟以现在的我而言,一方面有前线工程师的视野和能力,另一方面又有专职讲师的时间和精力,并且我普通话说得好,声音稳定有磁性,天赋不能浪费。

希望接下来的时间里,能把讲师做好。说实话,现在课没人买没人听,我并不太担心。我觉得这是必经之路,想想郭德纲当年一后台人给一个听众(都不堪称“众”……)讲相声,也是这么熬过来的。我花了十多年时间才成为一个资深工程师,自然也要花很多时间,才可能成为一名优秀讲师,这很自然。同样借罗辑思维的话,终身学习知识服务,不是随随便便就能搞成的,一样需要专业人士专业服务。

所以现在我要做的,就是积累课程、积累用户、积累行业内的名气。我计划今年就干这个。所以我也是在跟时间赛跑:在全家饿死之前,把课程培训做起来。


最后再打个广告,明天我会开始第三次直播:《Web 永恒不变的主题:布局——Box,Flex,Grid》,这次要分享的内容是比较基础的 CSS 布局,除了回顾前几年妖魔鬼怪横行的布局 hack,还会展示 Flexbox 使用和最新的 Grid。对各种级别的前端同学都是很好的一堂课。欢迎大家光临,帮我推广也感激不尽。

另外我考虑接下来用3周时间,直播一个完整的项目,包括项目配置、webpack + babel + ES6、CSS 预处理、gulp 批处理,整个一套做下来,以及自动化测试、代码规范检查等等。这个得跟站方商量一下。

新番预告

下周四(4月6日)晚8点,我会继续在 SegmentFault 开播,内容是《Promise 的 N 种用法》。房间:https://segmentfault.com/l/1500000008757392。欢迎光临。

下周四(4月6日)晚8点,我会继续在 SegmentFault 开播,内容是《Promise 的 N 种用法》。

房间:https://segmentfault.com/l/1500000008757392

欢迎光临。

坦率地说,这次准备比上次投入精力更多,准备也更充分,试讲就准备讲两次(已经讲了一次)。同样 ¥10.24,这次比上次性价比更高。50P 的 Slide,准备了 20 份代码范例,应该是我近期最大的一笔投入了。


今天晚上把其中递归循环也写出来了,还是很不容易呀。


这个标题呢,开始我是想讲 Promise 相关,标题没想好,随便写了个,结果这会儿他们的直播数量还不太多,所以很轻易就给我通过了,然后还宣传了……所以我也就懒得改了,就这么着吧。

使用 Promise 封装 FileReader

使用 Promise 封装已有的 API 可以写出更好看的代码。本文演示如何封装 FileReader,以及如何使用。

Promise 在处理异步的时候是个很好的选择,可以减少嵌套层次,让代码更好读,逻辑更清晰。ES6 将其加入规范,jQuery 3.0 也修改实现向规范靠拢(3.0 发布公告)。一些新增元素比如 .fetch() 原生就 “thenable”,不过大多数以往的 API 还要依赖回调,这个时候,我们只要将它们重新封装,就能避开嵌套陷阱,享受 Promise 带来的愉悦体验。

Promise 一般用法

先来看下 Promise 的一般用法。

// 声明 Promise 对象
var p = new Promise(function (resolve, reject) {
  // 不管啥时候,该执行then了,就调用 resolve
  setTimeout(function () { 
    resolve(1);
  }, 5000);

  // 或者不管啥问题,就调用 reject
  if (somethingWrong) {
    reject('2');
  }      
});

// 使用 Promise 对象
p.then(function (num) {
  // 对应上面的 resolve
  console.log(num); // 1
}, function (num) {
  // 对应上面的 reject
  console.log(num); // 2
});

Promise 的驱动模型并不复杂:任何操作,假定它只有两个结果,成功或者失败。那么只需要在合适的时间调用合适的程序,进入合适的后续步骤即可。.then() 顾名思义,就是下一步的意思,当前面的 Promise 有了结果——即调用 resolve 或者 reject——之后,就启动对应的处理函数。

Promise 实例创建后就会开始执行,判定结果需要我们自己来,比如加载成功,或者满足某个条件,等等。通过串联 .then() 则可以完成一系列操作。每次调用 .then() 都会创建一个新的 Promise 实例,它会静静等待前面的实例状态改变后再开始执行。

封装 FileReader

接下来开始封装。思路很简单,FileReader 除了提供各种 read 方法,还有几个事件钩子,其中 onerroronload 很明显可以作为判断任务是否完成的依据。加载成功的话,就需要用到文件内容,所以将文件或文件内容传递到下一步也十分必要。

最后完成的代码如下:

function reader (file, options) {
  options = options || {};
  return new Promise(function (resolve, reject) {
    let reader = new FileReader();

    reader.onload = function () {
      resolve(reader);
    };
    reader.onerror = reject;

    if (options.accept && !new RegExp(options.accept).test(file.type)) {
      reject({
        code: 1,
        msg: 'wrong file type'
      });
    }

    if (!file.type || /^text\//i.test(file.type)) {
      reader.readAsText(file);
    } else {
      reader.readAsDataURL(file);
    }
  });
}

为了能真正派上用场,里面还有一些验证文件类型的操作,不过跟本文主旨无关,略过不表。这段代码的核心是创建一个 Promise 对象,等待 FileReader 读取完成后调用 resolve 方法,或者出现问题时调用 reject 方法。

Github Gist 里也放了一份。

使用刚才封装好的函数

接下来就可以在项目中使用了:

reader(file)
  .then(function (reader) {
    console.log(reader.result);
  })
  .catch(function (error) {
    console.log(error);
  });

.then() 支持两个参数,第一个在 Promise 成功时启动,第二个自然在失败时启动。用 .catch() 可以实现同样地效果。Promise 的好处除了可读性更佳以外,返回的 Promise 对象还可以任意传递,继续进行链式调用,有很大想象空间。

继续 .then()

于是我们不妨串联更多操作(本来想写个断点续传的,回头再说吧):

reader(file)
  .then(function (reader) {
    return new Promise(function (resolve, reject) {
      // 就随便暂停个5秒吧……
      setTimeout(function () {
        resolve(reader.result); 
      }, 5000);
    });
  })
  .then(function (content) {
    console.log(content);
  });

总结

这其实是我第一次用 Promise,上次翻译 jQuery 发布公告的时候我它也只是一知半解,对它的解读也糊里糊涂。我很喜欢在业余项目中学习使用新技术,最近开发 Chrome 插件的时候就尝试了一把,感觉不错。使用过程比我想象的复杂也比我想象的简单,这套设计很棒,能解决不少实际问题,也给了我很大启发,将来我应该会把很多地方的实现都做这样的修改。


参考文献

除去第一段的各个链接,还有一些文章值得一看。

ECMAScript 6 入门:Promise 对象