|
| 1 | +2017-07-05 GitChat 答疑交流 |
| 2 | +======== |
| 3 | + |
| 4 | +## 1. 异步函数在 gulp 里的应用? |
| 5 | + |
| 6 | +**答:** |
| 7 | + |
| 8 | +我们知道,gulp 在需要顺序执行的时候,有三个方式: |
| 9 | + |
| 10 | +1. 调用 callback |
| 11 | +2. 返回一个 stream 或者 vinyl file |
| 12 | +3. 返回一个 Promise 对象 |
| 13 | + |
| 14 | +所以这个时候,应用异步函数再合适不过 |
| 15 | + |
| 16 | +```javascript |
| 17 | +gulp.task('task', async () => { |
| 18 | + let readFile = util.promisify(fs.readFile); |
| 19 | + let content = await readFile('sfbj.txt'); |
| 20 | + content = content.replace('old', 'new'); |
| 21 | + return content; |
| 22 | +}); |
| 23 | +``` |
| 24 | + |
| 25 | +## 2. 异步函数在 H5 项目中的应用? |
| 26 | + |
| 27 | +**答:** |
| 28 | + |
| 29 | +H5 项目中,动画的比重很大,有些动画有顺序要求,这个时候,用 Promise 来处理就非常合适。 |
| 30 | + |
| 31 | +这里提供一个我写的函数供大家参考: |
| 32 | + |
| 33 | +```javascript |
| 34 | +function isTransition(dom) { |
| 35 | + let style = getComputedStyle(dom).getPropertyValue('transition'); |
| 36 | + return style !== 'all 0s ease 0s'; |
| 37 | +} |
| 38 | + |
| 39 | +export function next(dom) { |
| 40 | + let eventName = isTransition(dom) ? 'transitionend' : 'animationend'; |
| 41 | + return new Promise(resolve => { |
| 42 | + dom.addEventListener(eventName, function handler(event) { |
| 43 | + dom.removeEventListener(eventName, handler); |
| 44 | + resolve(event); |
| 45 | + }, false); |
| 46 | + }); |
| 47 | +} |
| 48 | +``` |
| 49 | + |
| 50 | +这个函数会根据元素的样式判定是使用 `transition` 动画还是 `animation` 动画,然后侦听响应事件,在事件结束后,执行下一步。 |
| 51 | + |
| 52 | +使用的时候,可以先把写好的动画样式绑上去,然后侦听: |
| 53 | + |
| 54 | +```javascript |
| 55 | +this.actions = next(loading) |
| 56 | + .then(() => { |
| 57 | + el.classList.remove('enter'); |
| 58 | + }) |
| 59 | + .then(() => { |
| 60 | + wukong.classList.add('in'); |
| 61 | + wukong.style.transform = 'translate3d(0,0,0)'; |
| 62 | + return next(wukong); |
| 63 | + }) |
| 64 | + .then(() => { |
| 65 | + let bufu = this.queue.getResult('bufu'); |
| 66 | + bufu.className = 'bufu fadeInUp animated'; |
| 67 | + el.appendChild(bufu); |
| 68 | + return next(bufu); |
| 69 | + }) |
| 70 | + .then(() => { |
| 71 | + let faxing = this.queue.getResult('faxing'); |
| 72 | + faxing.className = 'faxing fadeInUp animated'; |
| 73 | + el.appendChild(faxing); |
| 74 | + return next(faxing); |
| 75 | + }) |
| 76 | + .then(() => { |
| 77 | + let bg = this.queue.getResult('homepage'); |
| 78 | + bg.className = 'bg fadeIn animated'; |
| 79 | + el.insertBefore(bg, el.firstChild); |
| 80 | + return next(bg); |
| 81 | + }); |
| 82 | +``` |
| 83 | + |
| 84 | +在这种场景,使用 Promise 会比使用异步函数更方便维护。 |
| 85 | + |
| 86 | +## 3. promise有什么缺陷,await是怎么解决的? |
| 87 | + |
| 88 | +**答:** |
| 89 | + |
| 90 | +Promise 的特性之一,便是:不增加新的语言元素,在现有语言框架下解决问题。 |
| 91 | + |
| 92 | +所以,Promise 只能解决代码不好阅读,不易维护的问题,面对语言本身的问题,它也无能为力。 |
| 93 | + |
| 94 | +这个问题,就是异步回调在执行的时候,会切断前后栈的联系。 |
| 95 | + |
| 96 | +在文章的第一章,第二小节,对这个问题有很具体的描述。 |
| 97 | + |
| 98 | +(摘原文) |
| 99 | + |
| 100 | +异步函数(await/async)是新的语法,改变了运行时,所以可以继续检索堆栈,完全不会有这方面的问题。 |
| 101 | + |
| 102 | +## 4. 异步编程在前后端分离的场景下,主要有哪些作用?前后端分离下的API该如何管理才合理? |
| 103 | + |
| 104 | +这个问题我有点懵,在我看来,尤其是前后端分离的场景,几乎都必须要用异步编程来处理,Promise 和异步函数在这里都能发挥巨大的作用。 |
| 105 | + |
| 106 | +比如,一个后台,用户打开后,应该判断用户的登录状态,然后取用户的设置、用户的消息、用户的代办事项等,写成伪代码就是: |
| 107 | + |
| 108 | +```javascript |
| 109 | +let user = new User(); |
| 110 | +async function onLoad() { |
| 111 | + let info = await checkUserStatus(); |
| 112 | + if (info) { |
| 113 | + user.info = info; |
| 114 | + } else { |
| 115 | + router.go('/login'); |
| 116 | + user.info = await login(); |
| 117 | + } |
| 118 | + user.settings = await getUserSettings(); |
| 119 | + user.messages = await getUserMessages(); |
| 120 | + user.todos = await getUserTodos(); |
| 121 | + .... |
| 122 | +} |
| 123 | + |
| 124 | +onLoad(); |
| 125 | +``` |
| 126 | + |
| 127 | +## 5. 有了Async还有无必要学习Generator? |
| 128 | + |
| 129 | +坦白说,我认为,没有。我还没遇到非用 Generator 才能解决的问题,也没有遇到过用 generator 能解决的更漂亮的问题 |
| 130 | + |
| 131 | +如果你现在时间比较紧张,我建议先从 Promise -> 异步函数学起。将来有时间再学不迟。 |
| 132 | + |
| 133 | +## 6. 初始化数据库时, 需要队列来保证代码按照步骤执行, 期间也需要捕获错误, 请问老师, 这个场景Async还是Promise较好呢? |
| 134 | + |
| 135 | +这个问题我觉得比较好,哈哈。 |
| 136 | + |
| 137 | +不过回答这个问题比较困难,还是要看场景。我们不妨再对比一下 Promise 和异步函数的优劣(第3章第二节): |
| 138 | + |
| 139 | +Promise 的优势:**身为队列,可以向任何地方传递。** |
| 140 | + |
| 141 | +异步函数的优势:**好写好读,方便调试。** |
| 142 | + |
| 143 | +所以我认为这里是,如果你会有后续操作,比如在建立数据库连接的时候允许用户操作,那么 Promise 队列可能更合适,因为你可以把用户操作追加在队列后面,很方便,不需要你管理连接的状态;否则的话,可能异步函数更合适,少写就是力量。 |
| 144 | + |
| 145 | +## 7. (问题3)请问老师,这里的“启用一个新栈”,可以理解为eventloop么?JS引擎返回的回调函数的结果,不会push到原程序运行的栈内么? |
| 146 | + |
| 147 | +很好的问题,切中本质。 |
| 148 | + |
| 149 | +首先,不能理解成 eventloop,eventloop 其实是方便用户操作设计的,它是“等待消息,处理消息”两者的循环往复,和 Node.js 里的时间回调有很大不同。后者更接近的模型是 jQuery 的 jsonp。我们要把 Node.js 看成两部分:V8 和其它,所以这里的异步回调是把回调函数的引用丢给 V8,然后“其它”完成操作后,要求 V8 调用之前注册的回调函数。 |
| 150 | + |
| 151 | +然后我们又要回归栈的本质:先进后出。比如下面这段代码: |
| 152 | + |
| 153 | +```javascript |
| 154 | +function b() { |
| 155 | + doAsync(); // 异步函数 |
| 156 | + let abc = 123; |
| 157 | + return abc; |
| 158 | +} |
| 159 | +function a() { |
| 160 | + b(); |
| 161 | + return 123; |
| 162 | +} |
| 163 | +a(); |
| 164 | +``` |
| 165 | + |
| 166 | +如果我们要保证异步的执行,即 `let abc = 123` 及之后的执行,就必须正常让函数 `doAsync` 出栈,然后 `b` 出栈,然后 `a` 出栈。所以回调函数的结果,是很难直接 push 到原先的栈中的。 |
| 167 | + |
| 168 | +当然现在问题解决了,不过我没有去看里面的实现,将来有机会看了再分享一次。 |
0 commit comments