一路走来。很多东西都是随性而写。很多问题,并未能理解的深刻。问题就像递归一下,要解释这个问题,不得不去解释另一个问题;也怪自己不能很合适的抽象。
今天又是一个乱炖。
function arguments
1 | function yes(){} |
函数就是一个值,可以做参数,也可以当返回值。也因此,在JavaScript中,函数参数大行其道,尤其是 callback,无处不在。
1 | window.addEventListener("click", function() { |
再举一个栗子:
1 | function job1() {console.log("job1 !!!");} |
我也不知道为啥举了这个栗子,哎。人家都说回调多了,就进了地狱,我就是尝试一下地狱。
复制粘贴大法好
Callback function A callback function is a function passed into another function as an argument, which is then invoked inside the outer function to complete some kind of routine or action.
粘贴一些小的知识点
打开 谷歌浏览器的 console。我们来看点东西。我这里是一句一句执行的。
没毛病。haha的 Scopes 只有 Global,我这里没有定义 t。
擦!haha的 Scopes 变了,[Script, Global]。这就是 let 搞得鬼。let 偷偷修改了作用域。(const 同理)
这也就是为什么 t 有值,但是在 this 中找不到!!!
上面说了 let 偷偷修改了作用域,不仅仅是新建作用域这么简单
下图中,Scopes 多了 Block。第一个函数是被优化了,两个其实应该是一样的。作用域链上没用到的,谷歌浏览器直接踢了。
为毛一个多了 Script,一个多了 Block;简而言之,一个是修改,一个是新建(未来会不会有什么变化,到时再看)。这里没有谈及其他的特性,Scoping,TDZ,redeclaration。你以为新语法只有好处拿???不得不说,带来了不少新的问题。有空再搞一搞吧。请访问化学家兼计算机科学家的const 和 let 到底定义到哪儿去了?1
2
3
4
5
6
7
8
9
10
11<script>
let t = "go";
function ttt(){return t;}
console.dir(ttt);
{
let t = "go";
function ttt(){return t;}
console.dir(ttt);
}
</script>
JS代码所处的环境就像这样
Global.cpp_initialize(/** **/)
,这一句是外界(浏览器? JSVM?对不起,我不知道)提供,换句话说,我们的这些代码也处在一个外界提供的作用域;由外界提供 this,浏览器中就是 window。
我以前说过 this is not important ,是想说,很多时候你就不必关心 this 。arrow functions 还想探究一下;还是算了,扯着扯着就扯到蛋了,越来越远。感觉又挖坑了!我今天是来搞 function 的,callback 的。打算改写下 callback 的样子。
从网上搜了不少文章,谢谢各位前辈,写下自己的理解。
(写代码好吃力,各种错误;去网上粘点吧)
这里,我先发送一个请求,完了再发送第二个请求。(辣鸡代码,未处理异常,慎重!)
- callback
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20var url1 = "https://yesno.wtf/api";
var url2 = "https://yesno.wtf/api";
function request(url, f) {
var xhr = new XMLHttpRequest();
xhr.addEventListener('load', function (e) {
f(xhr.response);
});
xhr.responseType = "json";
xhr.open('get', url);
xhr.send();
}
request(url1, function cb1(r) {
console.log("1st: %o", r);
request(url2, function cb2(r) {
console.log("2nd: %o", r);
// request(url3, function cb3() {});
});
});
如果想依次发多个请求,把自己就回调进去了,真是焦头烂额。
- promise
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24const url1 = "https://yesno.wtf/api";
const url2 = "https://yesno.wtf/api";
function requestP(url) {
return new Promise((s, f) => {
let xhr = new XMLHttpRequest();
xhr.addEventListener('load', function (e) {
s(xhr.response);
});
xhr.responseType = "json";
xhr.open('get', url);
xhr.send();
});
}
requestP(url1)
.then(v=>{
console.log("1st: %o", v);
return requestP(url2);
})
.then(v=>{
console.log("2nd: %o", v)
// return requestP(url3);
});
这样就把 回调 放在 then 里面了。 // 看了一眼别人的代码,哦,懂了。自己写起来,半天挤不出来。唉……
- generator
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28const url1 = "https://yesno.wtf/api";
const url2 = "https://yesno.wtf/api";
function requestN(url) {
var xhr = new XMLHttpRequest();
xhr.addEventListener('load', function (e) {
//console.log(xhr.response);
rg.next(xhr.response);/*我们再次启动 rg,并把结果塞回去*/
});
xhr.responseType = "json";
xhr.open('get', url);
xhr.send();
}
function* requestG() {
let r1 = yield requestN(url1);
console.log("1st: %o", r1);
let r2 = yield requestN(url2);
console.log("2nd: %o", r2);
/*
let r3 = yield requestN(url3);
console.log("3rd: %o", r3);
*/
}
let rg = requestG();
rg.next(); // 启动
rg 执行到了 yield 那里,就会计算后面的表达式,然后吐出一个这样的对象 {value: 值, done: false}
,rg 就休息了(我也感觉困了);当 xhr load 以后,执行语句 rg.next(xhr.response),就会再次唤醒 rg ,并且 yield 返回值就是此时传进去的参数,也就是说把 xhr.response 给了 r1;下次遇到 yield ,rg 又歇菜了。 (至于 rg.throw,rg.return 是干嘛的,我还没用过呢。)
简单说,yield 先要吐出来一个值;再次使用 next 启动时,yield 还要返回一个值,这货还脚踩两只船。吐出来的值(吐一个promise也行,吐一个函数也行),我们这里没用到;我们这里就只利用了 yield 返回值,也就是我们再次启动时,传进去的值,这样才能把结果给 r1、r2。
1 | const url1 = "https://yesno.wtf/api"; |
代码写的虽然和狗屎一样,但是这次 yield 吐出了一个 promise 的值,(上此吐出来的值,我们并未在意)。我们在 then 中 调用了 next 方法,再次启动 generator,启动的时机不同。
- async/await
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28const url1 = "https://yesno.wtf/api";
const url2 = "https://yesno.wtf/api";
function requestP(url) {
return new Promise((s, f) => {
let xhr = new XMLHttpRequest();
xhr.addEventListener('load', () => {
s(xhr.response);
});
xhr.responseType = "json";
xhr.open('get', url);
xhr.send();
});
}
async function requestA() {
let r1 = await requestP(url1);
console.log("1st: %o", r1);
let r2 = await requestP(url2);
console.log("2nd: %o", r2);
/*
let r3 = await requestP(url3);
console.log("3rd: %o", r3);
*/
}
requestA();
await 和 yield 很像,不过更过分。 await “等到”了完成了的 Promise,就会再次启动,并把值返回。
这四个功能基本相同,但写法却有很大不同,主要是越来越简洁清晰,爽快。
yield; next
看看它们的嘴脸。1
2
3
4
5delete Function; // Don't worry, be happy!
Function = Object.getPrototypeOf(function () {}).constructor; //这些内置对象,不在三界五行之中。你看它的“定义域”就知道了。
GeneratorFunction = Object.getPrototypeOf(function* (){}).constructor;
AsyncFunction = Object.getPrototypeOf(async function () {}).constructor;
AsyncGeneratorFunction = Object.getPrototypeOf(async function* () {}).constructor;
关于generator的自动执行。 yield 吐出来的那个值,在这个值完成了相关任务后,调用 next(result) 再返回结果。具体参考 co 库(听别人说的)。如今还是连字符串操作都不熟练,没心思吹了,打基础去了。
突然想到了这几个 request 函数,比如第一个。request执行了,内部 xhr 去哪里了?虽然上面挂着 load 事件。惊出了我一身冷汗,我再仔细想想。
1 | function request(url, f) { |
成天就知道玩弄概念。还是该多练。熟能生巧。
1 | for(let i=0; i<10000; i++) { |
先放一万个事件到队列。看结果,xhr 是在半路输出的,插队了。 xhr 很及时,也可能和 timeout 不在一个队列。