-
Notifications
You must be signed in to change notification settings - Fork 11
Description
Promises简介
原文地址:http://www.promisejs.org/intro/
本文假定读者具有一定的JavaScript基础。不论是刚接触异步编程还是对此有一定经验的人都适合阅读。
初衷
我们希望代码能异步执行,因为当代码在同步执行时,(在客户端应用中)用户界面(user interface,简称UI)会被卡住或者(在服务器端应用中)请求得不到及时响应。解决方案之一是多线程,但是多线程又会带来其他问题,而且JavaScript中也不支持。
能让代码异步起来的最简单的方法之一就是传递回调函数。也就是NodeJS所在使用的。它能行得通,但是有一系列的问题。
1.You lose the separation of inputs and outputs to a function since the callback must be passed as an input
2.It is difficult to compose multiple serial or parallel operations
3.You lose a lot of helpful debugging information and error handling ability relating to stack traces and the bubbling up of exceptions
4.You can no longer use the built in control flow constructs and they must all be re-invented to work asynchronously
浏览器端有很多基于事件模型的解决方案,但这只能解决问题1,却解决不了2-4。
Promises就是为了解决问题1-3而产生的,而4将在ES6中也能得到解决。
基本方法
Promises的核心思想就是一个promise对象代表着一个异步操作的返回结果。它也许最终只是个抛出的错误也没关系。异步函数的返回值应是promise对象:
var prom = get('http://www.example.com')当请求http://www.example.com页面时,会发起一个异步请求,返回一个promise对象prom。
要从promise对象中获取请求的内容,我们使用done方法,它可以排入一个待执行的回调函数进队列,等到promise异步操作完成后,队列里的回调就会依次被执行。
var prom = get('http://www.example.com')
prom.done(function(content){
console.log(content)
})注意我们是怎么给done方法传递一个未执行的回调函数的,当异步操作结束后此回调仅会被执行一次。而done方法我们能执行多次,执行的或早或晚都没关系,但总是得到同样的结果。比如当异步操作已经完成之后,仍然还可以去执行它:
var cache = {}
function getCache(url){
if(cache[url]) return cache[url]
else return cache[url] = get(url)
}
var promA = getCache('http://www.example.com')
promA.done(function(content){
console.log(content)
})
setTimeout(function(){
var promB = getCache('http://www.example.com')
promB.done(function(content){
console.log(content)
})
}, 10000)当然了,如果去请求一个错误页面会报错并抛出异常。默认情况下,done方法只简单的抛出这些错误并使其适当的被日志记录,并(除了在浏览器环境中)中断程序。然而我们更喜欢以提供回调的方式来自由控制错误处理:
var prom = get('http://www.example.com')
prom.done(function (content) {
console.log(content)
}, function (ex) {
console.error('Requesting www.example.com failed, maybe you should try again?')
console.error(ex.stack)
})变形
通常,对于已经存在的一个特定promise对象,对它做一些操作后,想得到另外一个promise对象。Promises有个then方法,它类似Array对象的map方法。
function getJSON(url){
return get(url)
.then(function(res){
return JSON.parse(res)
})
}
getJSON('http://www.example.com/foo.json').done(function(res){
console.dir(res)
})注意then方法是怎么处理错误的,它能像同步代码一样抛出错误堆栈。还可以在调用then方法的时候传递处理错误的回调函数:
function getJSON(url) {
return get(url)
.then(function (res) {
return JSON.parse(res)
}, function (err) {
if (canRetry(err)) return getJSON(url)
else throw err
})
}
getJSON('http://www.example.com/foo.json').done(function (res) {
console.dir(res)
})处理错误的回调函数并不是要处理由JSON.parse抛出的错误,而是由调用get方法而抛出的一些错误,这些错误会被加以重试操作。注意下面是怎样从then方法返回promise对象和它被自动解开的
var prom = get('http://example.com/url-to-request')
.then(function(url){
return get(url)
})
.then(function(res){
return JSON.parse(res)
})
prom.done(function(finalResult){
console.dir(finalResult) // 这是真正的’最终结果‘
})组合
promise作为一个返回值的优势之一就是可以针对它进行一些有用的操作来拼接其他promise对象。all方法就是多数框架里都支持的这样的操作之一:
var a = get('http://www.example.com')
var b = get('http://www.example.co.uk')
var both = Promise.all([a, b])
both.done(function(res){
var a = res[0]
var b = res[1]
console.dir({
'.com': a,
'.co.uk': b
})
})当需要进行许多并行操作的时候,这样就显得特别有用。由此还也可以扩展成为数组形式的大型多任务操作:
function readFiles(files){
return Promise.all(files.map(function(name){
return readFile(name)
}))
}
readFile(['fileA.txt','fileB.txt','fileC.txt']).done(function(filesContents){
console.dir(filesContents)
})当然,用then方法仍然可以进行串行操作:
get('http://www.example.com').then(function(res){
console.log('.com')
console.dir(res)
return get('http://www.example.co.uk')
}).done(function(res){
console.log('.co.uk')
console.dir(res)
})展开想象,还可以使用then方法来串行处理数组形式的大型多任务操作:
function readFiles(files){
var result = []
// 创建一个以null为初始值的promis对象
var ready = Promise.from(null)
files.forEach(function(name){
ready = ready.then(function(){
return readFile(name)
}).then(function(content){
result.push(content)
})
})
return ready.then(function(){
return result
})
}
readFiles(['fileA.txt', 'fileB.txt', 'fileC.txt']).done(function(filesContents){
console.dir(filesContents)
})已成型库/下载
现在已经有大量的遵守Promises/A+规范的成型库,并非所有库都提供了done方法和Promise.all方法。可以根据实际需求来挑选适合你的库,下面是两个推荐的库: