Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

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‎

Lines changed: 2 additions & 0 deletions
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‎

Lines changed: 168 additions & 0 deletions
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)

AltStyle によって変換されたページ (->オリジナル) /