关于promise,网上的介绍很多,可以说是callback hell的解决方案。由于使用了promise一段时间了,想了很多,这里稍微聊聊。了解promise的原理和用法之后,写程序的时候就会容易很多。
首先,关于promise的方法,显而易见,有两个then和catch
var promise = {
then:function(){},
catch:function(){}
}
then用于成功后的回调,而catch则返回err事件。而这两个方法是如何工作的呢?
最初
我们先定义一个todo方法
function todo(cb){
var v = "this is cb"
cb(v)
}
假如我们要直接调用的话,是这样的
todo(function(v){
console.log("got it :",v)
})
如果一直这样cb下去,就会陷入cb hell了。如果使用promise的话,应该是这样调用的
function todo(){
var v = "this is cb"
return{
then:function(cb){
cb(v)
}
}
}
todo().then(function(v){
console.log("got it :",v)
})
解释什么是promise
还是用代码来说话,
function Promise(fn){
var callback
this.then = function(cb){
callback = cb
}
function resolve(v){
setTimeout(function(){
callback(v)
},0)
}
fn(resolve)
}
function todo(){
return new Promise(function(resolve){
var v = "this is cb"
resolve(v)
})
}
这里要注意一下resolve之中为什么要用setTimeout。假如不这样做,而是直接
function resolve(v){
callback(v)
}
程序就会报错,因为程序会在this.then之前得到resolve,而这个时候callback还未=cb。至于为什么,只要了解什么是event loop之后就知道了,可以看下https://developer.mozilla.org/en-US/docs/Web/JavaScript/EventLoop。
promise的状态
我们已经写完了一个简单的promise应用,但是还不够,依旧会碰到许多callback的问题。所以对于promise,需要给他定义两个状态(还有第三个状态rejected,处理失败事件,后面再讨论)
1. pending//等待
2. fulfilled//成功
function Promise(fn){
var state ="pending"
var v
var deferred
function resolve(newV){
v = newV
state ="fulfilled"
if(deferred){
handle(deferred)
}
}
function handle(onFulfilled){
if(state==="pending"){
deferred = onFulfilled
return
}
onFulfilled(v)
}
this.then = function(onFulfilled){
handle(onFulfilled)
}
fn(resolve)
}
function todo(){
return new Promise(function(resolve){
request("http://www.baidu.com",function(err,res,body){
if(!err&&res.statusCode==200) resolve(res)
else reject(err)
})
})
}
todo().then(function(v){
console.log(v)
})
这里我用了request依赖,比较容易解释关于回调的问题。
这里整个过程是这样的
todo()
- Promise,todo
- 然后由于todo(),开始运行Promise的方法
- state=“pending”,v=undefined,deferred=undefined,resolve,handle,this.then,fn(resolve)
- request(“http://www.baidu.com“)
todo().then()
- handle(onFulfilled)
- if(state===”pending”){….}
- 然后等request得到回调之后
- if(!err&&res.statusCode==200) resolve(res)
- v = newV,state =”fulfilled”,这个时候deferred=onFulfilled = function(){}
- handle(deferred)
- onFulfilled(v)
- console.log(v)
于是乎,我们就得到了请求的结果了,但是,像这样的Promise方法还不能满足我们日常的需要,因为我们一般使用Promise的时候代码看起来应该是这样的:
todo()
.then(function (v) {
var s = []
s.push(v)
return s
})
.then(function (v) {
s.push(v)
return s
})
.then(function (v) {
console.log(v)
})
所以我们需要再改下代码。让每个return 能作为下一个then的输入才行。
但是如何做呢
很显然,我们需要用到递归的想法来实现这样的,每使用一次.then(),实例化一次Promise
function Promise(fn) {
var state = "pending"
var v
var deferred
function resolve(newV) {
if(newV && typeof newV.then === 'function') {
newV.then(resolve);
return;
}
v = newV
state = "fulfilled"
if (deferred) {
handle(deferred)
}
}
function handle(h) {
if (state === "pending") {
deferred = h
return
}
if(!h.onFulfilled){
h.resolve(v)
return
}
var ret =h.onFulfilled(v)
h.resolve(ret)
}
this.then = function (onFulfilled) {
return new Promise(function(resolve){
handle({
onFulfilled:onFulfilled,
resolve:resolve
})
})
}
fn(resolve)
}
好了,关于Promise就先聊到这里。关于Pormise的依赖,建议使用bluebird
https://github.com/petkaantonov/bluebird