Skip to content

Commit 64b5337

Browse files
committed
增加交流问答的内容
Signed-off-by: Meathill <meathill@gmail.com>
1 parent 4b24c7e commit 64b5337

File tree

2 files changed

+170
-0
lines changed

2 files changed

+170
-0
lines changed

01-2-issue.md

+2
Original file line numberDiff line numberDiff line change
@@ -115,6 +115,8 @@ function (err, result) {
115115

116116
我们知道,函数执行是一个“入栈/出栈”的过程。当我们在 A 函数里调用 B 函数的时候,JS 引擎就会先把 A 压到栈里,然后再把 B 压到栈里;B 运行结束后,出栈,然后继续执行 A;A 也运行完毕后,出栈,栈已清空,这次运行结束。
117117

118+
这个时候,我们如果中断代码执行,可以检索完整的堆栈,完整的作用域链(闭包),获取任何我们想获取的信息。
119+
118120
可是异步回调函数(包括事件处理函数,下同)不完全如此,比如上面的代码,无论是 `fs.readdir` 还是 `fs.readFile`,都不会直接调用回调函数,而是继续执行其它代码,直至完成,出栈。真正调用回到函数的是引擎,并且是启用一个新栈,压入栈成为第一个函数。所以如果回调报错,一方面,我们无法获取之前启动异步计算时栈里的信息,不容易判定什么导致了错误;另一方面,套在 `fs.readdir` 外面的 `try/catch`,也根本捕获不到这个错误。
119121

120122
结论:回调函数的栈与启动异步操作的栈断开了,无法正常使用 `try/catch`

qa.md

+168
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,168 @@
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

Comments
 (0)