blog
blog copied to clipboard
I promise, I resolve
Promise是当今大热, 很多人选择Promise来控制自己的异步流程, Promise甚至还进了下一代标准, 在最新的浏览器(chrome 32, 最新的Firefox)中已经自带
要注意的是Promise目前属于DOM Object, 虽然是es6的标准, 但只要不进v8, nodejs就不可能内置, (还是yield好
Promise的API可以参照MDN中的文档, 以及html5rock中的这一篇
首先要知道Promise最基本的用法, 把原生的Promise替换掉, 效果还一样就说明大致成功了
最简单的promise用法
new Promise(function(resolve) {
setTimeout(function() {
resolve('hello')
}, 1000)
}).then(function(d) {
console.log(d) // hi
})
可以看出Promise的理念就如其名, 先把异步函数放在Promise对象中, 在then函数中执行
换句话说, 就是我给你这个承诺, 但不马上执行, 先存起来, then的时候就执行, 执行完后告诉你是resolve了还是reject了
then函数
可以说Promise对象中最重要的属性就是then, then是一个函数, 其参数为then(resolve, reject)
resolve和reject也是函数, resolve函数中的参数就是上一次执行带来的参数, 注意, 一般来说是没有第一个参数是error的习惯的, 因为promise有reject
Promise当然不会是先存起来再then执行这么简单, 这也太弱了, 它更大的特点是可以不断的then
链式then
new Promise(function() {
...
}).then(resolve).then(resolve).then(resolve)...
Promise的魅力就是它可以链式的then下去, 当然像上面这样then只有第一个有用, 后面都是空then
后面的then的执行内容取决于前一个then中resolve的返回值, 看这个例子
new Promise(function(resolve) {
setTimeout(function() {
resolve('haha')
}, 1000)
}).then(function(d) {
console.log(d) // haha
return new Promise(function() {
setTimeout(function() [
resolve('hahaha')
}, 1000)
})
}).then(function(d) {
console.log(d) // hahaha
})
上面例子中第一个then返回了一个新的Promise对象, 然后接下来的then就能获取上面resolve的参数了
可以想见, Promise使用如此广泛, 却很少看到, 因为Promise都用在库的内部, 比如jQuery的ajax中, 他能随意中断或者执行, 不过是因为它每一步都返回一个Promise对象
那Promise对象到底有何特征呢? then()是如何判断resolve的return值是Promise对象呢?
其实Promise对象特征就一个, 上文也说过了, 就是有then属性
不信来做个试验
new Promise(function(resolve) {
setTimeout(function() {
resolve('haha')
}, 1000)
}).then(function(d) {
console.log(d) // haha
return {
then: function(resolve) {
setTimeout(function() {
resolve('hahaha')
}, 1000)
}
}
}).then(function(d) {
console.log(d) // hahaha
})
上面例子的第一个resolve仅仅是返回了一个带then一个属性的对象, 也可以链式的then下去
可见不管Promise的库有多少, 不管他用catch还是fail来表示错误, 不管是怎样的接口, then是肯定要有的, 或者说, then就是promise的一切
知道了这个我们就可以动手实现一个自己的Promise了
(function(exports) {
exports.Promise = Promise
var stack = []
var resolves = []
function Promise(fn) {
stack.push(fn)
}
Promise.prototype.then = function(resolve) {
resolves.push(resolve)
run()
return this
}
function resolve(val) {
var fn = resolves.shift()
var res = fn(val)
if (res && res.then) {
stack.push(res.then)
run()
}
}
function run() {
var fn = stack.shift()
if (typeof fn === 'function') {
fn(resolve)
}
}
})(window)
去年小米面试说用20行实现一个Promise, 想必我这个还有没精简的地方, 我那时随便写了个不知道啥的东西交上去, 至今想起还有点羞愧
这里面最关键的函数就是resolve, 但功能简单明确, 执行resolve函数, 判断其返回值有没有then属性, push进执行函数数组中, 等待下一个run
闭包中存放两个数组, 一个是stack, 即第一个Promise的参数加上所有的返回值的then. 第二个是resolves参数, 是所有的then后面的resolve函数的集合
Promise该如何用呢?
我们还是写一个delay函数
function delay(time) {
return {
then: function(resolve) {
setTimeout(function() {
resolve(time)
}, time)
}
}
}
这个delay比我们之前看到的toThunk型delay还多套了一层, 光这一点就说明Promise不如thunk
实现一个简单的waterfall流程
new Promise(delay(600).then).then(function(d) {
console.log(d) // 600
return delay(d + 200)
}).then(function(d) {
console.log(d) // 800
return delay(d + 200)
}).then(function(d) {
console.log(d) // 1000
return delay(d + 200)
})
上述Promise都完全没有涉及reject, 就已经有点复杂了, Promise说到底, 真的是解决了缩进, 也仅仅是解决了缩进, 没有从根本上简化异步流程, 要想真爽, 还得靠yield