在javascript中函数是绝对的一等公民,可以自定义属性,自定义一切。
定义函数
- 直接函数声明
var q = myFunc()
function myFunc(){
// arrangements
return 1
}
myFunc()
这种方式声明对位置没有要求,可以放置于上下均可调用
2. 字面定义函数表达式
var myFunc = function () {
// arrangements
return 1
}
var q = myFunc()
这种方式则需遵守自上而下
原因: 解析器会先读取函数声明,并使其在执行任何代码之前可以访问;而函数表达式则必须等到解析器执行到它所在的代码行才会真正被解释执行。
立即执行函数
在js中,通常会用到立即执行:
(function () {
})()
函数可定义属性
function myFunc () {}
myFunc.myAttr = 'this is my attr'
console.log(typeof myFunc) // function
console.log(myFunc.myAttr) // 'this is my attr'
重要的函数 — setTimeout
setTimeout能够充分展现js的异步特性
当我们定义setTimeout(function () {}, 0)后
相当于让这个进入等待队列,直到主要进程运行完毕,其才会运行,也可以用他来模拟一些异步请求。
解决回调地狱,安排回调函数 (可读可不读)
参考网址 (介绍了解决的几种规范)
在javascript当中,会出现异步任务,如果只是单纯有一个异步任务,那我们只需要一个回调函数去拿数据即可。
function getData(url, callback) {
setTimeout(function () {
//data...
callback(data)
}, 1000)
}
但如果要安排取到数据之后的种种行为,我们就会很头疼。
callback(function (){
callback(function () {
callback(function () {
callback(function () {
callback...
})
})
})
})
这什么东西?
于是得想办法啊:
有人提出了几种promise规范,那下面就直接谈promise/A了,毕竟在未来一段时间里这是老大。
Promise对象
doPromise().then(data).then(data).then(data)
then then then then比callback callback确实清晰了不少,可我们该如何去实现它呢?
function Promise() {
}
首先肯定得有一个promise
var PENDING = 0;
var FULFILLED = 1;
var REJECTED = 2;
三种状态肯定也得有,PENDING就是说你没有收到数据的状态,而FULFILLED肯定是收到数据的,REJECTED则是你reject或出错后才想着去处理的情况了。
说了半天,讲了状态,怎么用then中的回调函数去接受异步数据呢?
其实很简单,在状态为PENDING时,我们调用then函数,创造一个handler,将其缓存至当前promise的handlers队列中即可,然后返回一个新的promise,在这个新的promise中接收我们当前的then函数return的值。换句话说,then函数在今后接收到数据的时候(resolve),会发动一系列连锁反应,首先then函数接收数据,如果then函数return了一个新的值,就会被下一个promise对象接收(resolve),重复如此。
等等,被下一个promise对象接收(resolve)同时传递到下一个then的callback中,你怎么办到的?
其实只要在处理的函数里加上一个ONFULFILLED状态情况就好了,即直接用该callback处理数据。
那么我们接下来就可以干活了:
function Promise (fn) {
var state = PENDING;
var value = null;
var handlers = [];
}
如果我们想给fn绑定resolve和reject方法怎么办?
我们可以用call就行
function Promise (fn) {
...
function resolve() {}
function reject () {}
fn.call(this, resolve, reject)
}
但怕的就是,万一有人又满脑子骚操作,连用两套resolve
function () {
resolve (data1)
resolve (data2)
}
也就是说,你同时开启两次,一个异步一个同步就真的很有趣了,你的异步数据就飞走了。
于是我们得阻止这种情况
function doResolve(fn, resolve, reject) {
}
索性我们直接规定一个绑定函数,函数名都写明为变量,直接往上传,然后里面动点手脚。
function doResolve (fn, resolve, reject) {
var once = false;
try {
fn(
function (val) {
if (once) return;
once = true;
resolve(val)
},
function (val) {
if (once) return;
once = true;
reject(val);
})
} catch (e) {
if (once) return;
once = true;
reject(e);
} // 用try便于追踪问题
}
好了,我们开始在promise里愉快的doResolve就是了,之后你尽管resolve,得到的不是第一个resolve里的值算我输。
function promise(fn) {
...
function resolve () {}
function reject () {}
doResolve(fn, resolve, reject);
}
好的,接下来该我们实现我们处理数据的函数了
var state = PENDING;
var value = null;
var handlers = [];
function resolve (val) {
value = val;
state = FULFILLED;
handlers.forEach(handle);
handlers = null;
}
function reject (val) {
value = val;
state = REJECTED;
handlers.forEach(handle);
handlers = null;
}
启动resolve后,我们的handle函数就已经蠢蠢欲动了:
function handle (handler) {
if (typeof handler !== 'object') throw Error('...');
if (state === PENDING) {
handlers.push(handler);
}
if (state === REJECTED) {
handler.onRejected(value);
}
if (state === FULFILLED) {
handler.onFULFILLED(value);
}
}
我们看到这个handler应该是个对象,而且得符合规范,拥有onRejected以及onFULFILLED属性。
我们可以知道,这个handler应该是在then的时候被缓存到队列中或者直接接收数据用的,而我们也知道,在then中我们不仅要缓存或者处理handler对象,我们还得返回一个新的promise对象,以便链式写法,以及达到像完成任务一样的顺序过程。这听起来简直不可能。
但程序就是一步步写出来的,我们可以先实现一个done函数,这个函数就是让handle函数保存一个handler对象。没有别的任何用途。
function done() {}
等等,我们用function done () {}这种形式会面临一种问题,就是done中的handle真的是原来的promise吗?
于是选择
this.done = function () {
}
这样放心不少继续干活
this.done = function (onFULFILLED, onRejected) {
setTimeout(function (
handle({
onFULFILLED: onFULFILLED,
onRejected: onRejected
})
) }, 0);
}
于是这个就出来了,再看看then:
this.then = function (onFULFILLED, onRejected) {
var self = this; // 保存当前的这个promise对象
return new Promise (function () {}) // 首先返回一个新的promise对象
}
好,我们返回了一个promise对象,可以实现then().then().then()这样的链式调用了。
this.then = function (onFULFILLED, onRejected) {
return new promise (function (resolve, reject) {
return self.done(function () {}, function () {}) // 此时我们通过这个来对之前的一个promise对象进行缓存或者数据处理,缓存的话,则到第一个promise开启resolve时,会启动这done传入的两个函数中的一个。
})
}
那么当resolve后,启动self.done中的函数,当中的函数返回的值又可以这样被下一个promise的resolve接收,也就是说被下一个promise直接处理:
this.then = function (onFULFILLED, onRejected) {
var self = this;
return new promise (function (resolve, reject) {
return self.done (function (result) {
try {
resolve(onFULFILLED(result));
} catch (e) {
reject(e); //被下一个promise捕获
}
},
function () {})
})
}
至此,then部分我们也完成了,接下来稍微补充一下错误处理就行:
this.then = function (onFULFILLED, onRejected) {
var self = this;
return new promise (function (resolve, reject) {
return self.done (function (result) {
try {
resolve(onFULFILLED(result));
} catch (e) {
reject(e); //被下一个promise捕获
}
},
function (error) {
try {
return resolve(onRejected(error));
} catch (e) {
reject(e);
}
})
})
之后我们就可以随便骚起来
var p = new promise(function (resolve) {
setTimeout(function () {
resolve(1000)
}, 1000)
})
.then (function (val) {
console.log(val); // 1000
return 10000
})
.then (function (val) {
console.log(val); // 10000
return 100000
})
.then (function (val) {
console.log(val); // 100000
})
当然,再骚也骚不过官方:
Promise.race([pA, pB, pC]).then(function (val) {
console.log(val);
})