Promise
前言
之前写 JavaScript 异步处理时,一般都会用回调函数(callback)方式来处理异步的结果,例如:
function f (callback) {
setTimeout(() => { // setTimeout 模拟耗时任务
const result = 'f';
callback(result)
}, 1500)
}
function f1 (res) {
console.log(res + '->' + 'f1')
}
f(f1) // f->f1
这样看起来很合理,也很好理解,但是,假如还有一个函数 f2,它需要根据 f1 的结果进行处理,如下:
// 修改f1
function f1 (callback) {
const result = 'f1'
return (res) => {
callback(res + '->' + result)
}
}
// 添加f2
function f2 (res) {
console.log(res + '->' + 'f2')
}
f(f1(f2)) // f->f1->f2
会发现逻辑处理变得很复杂,如果还有其他函数继续调用,代码将变的很难维护。
什么是 Promise
Promise 是一个对象,代表一个异步操作,有三种状态:
* pending(进行中)
* fulfilled (已成功)
* rejected(已失败)
只有异步处理后的结果,才能决定当前出于哪种状态,其他操作无法改变这个状态,而且一旦状态改变,就不会再变,任何时候都能得到这个结果,这就是 promise 承诺的意思。
Promise 对象状态的改变就只有两种:从 pending 变为 fulfilled,从 pending 到 rejected,即 resolve 和 reject 结果。
Promise 用法
Promise 是 ES6 的一个语言标准对象
一个实例:
const promise = new Promise (function(resolve, reject)) {
// TODO
if(/* 异步操作成功 */) {
resolve(result)
} else {
reject(error)
}
}
Promise 构造函数接受两个参数 resolve 和 reject,分别是在异步操作成功和失败的时候调用,然后作为参数传递出去。
如何把结果传递出去,使用 Promise的原型方法 then 或者 catch。
Promise.prototype.then
当 Promise 的状态发生改变时(resolve/reject),then 方法绑定的处理方法就会被调用。 then 方法包含两个参数,第一个参数绑定 resolve 状态的方法,第二个参数绑定 reject 状态的方法。
// 接上面的代码
promise.then(function (result) {
console.log(result)
}, function (error) {
console.log(error)
})
then 方法返回的是一个 Promise 对象,所以可以进行链式调用,如promise.then(/…/).then(/…/),第二个 then 已第一个 then 的结果作为参数。这个链式操作是 Promise 最大的作用。
利用 Promise 解决之前的回调问题:
function f() {
const promise = new Promise((resolve, reject) => {
setTimeout(function () { // setTimeout 模拟耗时任务
const result = 'f';
resolve(result);
}, 1500)
})
return promise;
}
function f1 (res) {
return (res + '->' + 'f1')
}
function f2 (res) {
console.log(res + '->' + 'f2')
}
f().then(f1).then(f2) // f->f1->f2
这样代码变得很好理解和维护了。
Promise.prototype.catch
catch 和 then 一样,只是它的参数只有一个,处理 reject 状态的回调函数,返回的也是一个新 Promise对象。
promise.then(function (result) {
console.log(result)
}).catch(function (error) {
console.log(error)
})
其他方法
Promise.all()
参数是具有 iterator 接口,且成员都是 Promise 对象的数据结构,返回一个新的 Promise 对象。
let p = Promise.all([p1, p2, p3])
p 的状态由 p1、p2、p3 决定:
* 如果 p1、p2、p3 的状态都是 resolve,那么 p 才是 resolve
* 只要其中一个是 reject,那么p的结果就是 reject
注意一点,这三者是并行处理的,设计的时候三者完全不相关。
Promise.race()
赛跑模式,参数和返回值和 all 方法一样:
let p = Promise.race([p1, p2, p3])
不同的是,p1、p2、p3之中一个对象率先改变状态,p的状态就跟第一个改变状态的结果而改变。
Promise.resolve()
会生成一个 Promise 对象,且它的状态为 resolve
let p = Promise.resolve('resolve');
p.then(function (res){
console.log(res) // 'resolve'
});
Promise.reject()
会生成一个 Promise 对象,且它的状态为 reject
let p = Promise.reject('error');
p.then(null, function (error){
console.log(error) // 'error'
});
async/await
async/await 是 es7定义的方法,是基于 Promise的实现。
在 es6 Promise 的实现中,我们会写 then 方法,传入一个回调函数去处理 resolve 和 reject 结果。使用 async/await 我们会这样做
let p = Promise.resolve('resolve');
async function func () {
let result = await p;
console.log(result)
}
func() // 'resolve'
Node 中的处理
重点是 util.promisify 的使用,栗子如下:
const fs = require('fs');
const util = require('util');
// promisify callbackify
const readFile = util.promisify(fs.readFile);
async function asyncRead () {
let res = await readFile('hello.txt');
return res;
};
asyncRead()
总结
总而言之,Promise 是一个新的语法糖,使用它能够方便我们写更好维护的代码。