es6 Promise 对象
ps:每个案例都是基于上一个改造的
一、Promise 简介
Promise 是一个对象,从它可以获取异步操作的消息
案例1
新建项目
[demo]
|-- src
|-- index.html
|-- index.js
|-- webpack.config.js
|-- package.json
demo/package.json
{
"name": "webpack",
"version": "1.0.0",
"description": "",
"scripts": {
"dev": "webpack-dev-server",
"build": "webpack -p"
},
"author": "",
"license": "ISC",
"devDependencies": {
"babel-core": "^6.26.0",
"babel-loader": "^7.1.2",
"babel-preset-es2015": "^6.24.1",
"less-loader": "^4.0.5",
"less": "^2.7.3",
"style-loader": "^0.19.0",
"css-loader": "^0.28.7",
"extract-text-webpack-plugin": "^3.0.2",
"html-webpack-plugin": "^2.30.1",
"webpack": "^3.10.0",
"webpack-dev-server": "^2.9.5"
},
"dependencies": {
"jquery": "^3.3.1"
}
}
demo/webpack.config.js
var webpack = require('webpack');
var HtmlWebpackPlugin = require('html-webpack-plugin');
var ExtractTextPlugin = require("extract-text-webpack-plugin");
module.exports = {
entry: { // 入口文件地址
index: './src/index.js'
},
output: { // 出口
path: __dirname + "/build", // 打包后的文件存放路径
filename: '[name].js' // 文件名,name即为entry的key
},
module: {
loaders: [
{
test: /.(js)$/, // js-loader
loader: 'babel-loader?presets[]=es2015'
},
{
test: /.css$/, // css-loader
loader: ExtractTextPlugin.extract('css-loader')
},
{
test: /.less/, // less-loader
loaders: ExtractTextPlugin.extract('css-loader!less-loader')
}
],
},
devServer: {
contentBase: './build',
inline: true,
hot: true,
before: (app) =>{
app.get('/one.json', function(req, res) {
res.json({
user: 'promise',
success: true
});
});
app.get('/two.json', function(req, res) {
res.json({
age: '11',
success: true
});
});
app.get('/three.json', function(req, res) {
res.json({
hobby: 'basketball',
success: true
});
});
}
},
plugins: [
new webpack.HotModuleReplacementPlugin(), // 启用热替换模块
new HtmlWebpackPlugin({
filename: 'index.html', // 生成的的html文件名
template: './src/index.html', // 被打包的html路径
chunks: ['index'] // 需要引入的js,对应entry的key
}),
new ExtractTextPlugin({ // 单独打包css
filename: '[name].css'
})
]
}
demo/src/index.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Promise</title>
</head>
<body>
</body>
</html>
demo/src/index.js
// console.dir() 可以显示一个对象所有的属性和方法。
console.dir(Promise)
运行 npm i , 运行 npm run dev , 打开 http://localhost:8080 效果如下
从上面的图中,可以看出 Promise 是一个构造函数,有 all、race、reject、resolve 这几个方法,原型上有then、catch等方法
Promise 的基本用法
// Promise 构造函数接受一个函数作为参数,该函数的两个参数分别是 resolve 和 reject
// resolve 异步操作执行成功后的回调函数
// reject 异步操作执行失败后的回调函数
const promise = new Promise(function(resolve, reject) {
// ... some code
if (/* 异步操作成功 */){
resolve(value);
} else {
reject(error);
}
});
// 用 then 方法分别指定 resolved 状态和 rejected 状态的回调函数
// then 方法可以接受两个回调函数作为参数, 第二个函数是可选的,不一定要提供
promise.then(function(value) {
// resolve 异步成功的操作
}, function(error) {
// reject 异步失败的操作
});
二、resolve、reject
案例2
demo/src/index.js
import $ from 'jquery';
const promise = new Promise(function(resolve, reject) {
console.log('Promise');
// 这里当 ajax 失败 ,或 success 不为 true 时,都认为是请求失败
$.ajax({
url: 'one.json',
success: (data = {}) => {
if (data.success) {
resolve(data);
} else {
reject(data);
}
},
error: (err = {}) => {
reject(err);
}
})
});
promise.then(function(value) {
console.log('resolved', value)
}, function(error) {
console.log('rejected', '请求失败')
});
效果如下
可以自己尝试下, 将 demo/webpack.config.js 里,one.json 请求的 success 改为 false , 如下
{
devServer: {
before: (app) =>{
app.get('/one.json', function(req, res) {
res.json({
user: 'promise',
success: false
});
});
}
}
}
效果如下 (重启服务)
案例2 中,Promise 新建后立即执行,所以首先 console 打印的是 Promise。然后,then 方法里的函数,就相当于是我们平时的回调函数,只有在 promise 这个异步任务完成后才能执行,所以 console 打印的 resolved 和 rejected 在后面输出。
案例3
先对 案例2 进行一个简单的封装
demo/src/index.js
import $ from 'jquery';
const getJSON = (opts) => {
// ajax 需要的参数都可以写在 opts 里
console.log('opts', opts)
const {
url = '',
type = 'GET',
} = opts;
const promise = new Promise(function(resolve, reject) {
$.ajax({
url,
type,
success: (data = {}) => {
if (data.success) {
resolve(data);
} else {
reject(data);
}
},
error: (err = {}) => {
reject(err);
}
})
});
// 返回一个Promise对象
return promise;
}
// 传入 url
getJSON({url: 'one.json'}).then(function(value) {
console.log('resolved one', value)
}, function(error) {
console.log('rejected one', '请求失败')
});
效果和 案例2 一样
demo/src/index.js
import $ from 'jquery';
const getJSON = (opts) => {
// ajax 需要的参数都可以写在 opts 里
console.log('opts', opts)
const {
url = '',
type = 'GET',
} = opts;
const promise = new Promise(function(resolve, reject) {
$.ajax({
url,
type,
success: (data = {}) => {
if (data.success) {
resolve(data);
} else {
reject(data);
}
},
error: (err = {}) => {
reject(err);
}
})
});
// 返回一个Promise对象
return promise;
}
// 这里为了代码看的清除,只写了 then 方法里的第一个函数
// 这样能够按顺序,输出每个异步回调中的内容
getJSON({url: 'one.json'})
.then((value) => {
// one.json 请求成功拿到的结果
console.log('resolved one', value);
// 进行 two.json 请求
// return 的是 Promise 对象
// 这里的数据能在下一个 then 方法中拿到
return getJSON({url: 'two.json'});
})
.then((value) => {
// two.json 请求成功拿到的结果
console.log('resolved two', value);
}
效果如下
第一个 then 方法中, return 的是 Promise 对象,也可以直接 return 数据,在下一个 then 方法中可以拿到数据,如下
demo/src/index.js
import $ from 'jquery';
const getJSON = (opts) => {
// ajax 需要的参数都可以写在 opts 里
console.log('opts', opts)
const {
url = '',
type = 'GET',
} = opts;
const promise = new Promise(function(resolve, reject) {
$.ajax({
url,
type,
success: (data = {}) => {
if (data.success) {
resolve(data);
} else {
reject(data);
}
},
error: (err = {}) => {
reject(err);
}
})
});
// 返回一个Promise对象
return promise;
}
getJSON({url: 'one.json'})
.then((value) => {
// one.json 请求成功拿到的结果
console.log('resolved one', value);
// return 的是 Promise 对象, two.json 请求的数据能在下一个 then 方法中拿到
return getJSON({url: 'two.json'});
})
.then((value) => {
// 直接返回数据
console.log('two', value);
return value;
})
.then((value) => {
// two.json 请求成功拿到的结果
console.log('resolved two', value);
},(value) => {
// two.json 请求失败拿到的结果
console.log('rejected two', value);
})
效果如下
可以自己尝试下,将 demo/webpack.config.js 里,two.json 请求的 success 改为 false , 如下
{
devServer: {
before: (app) =>{
app.get('/two.json', function(req, res) {
res.json({
age: '11',
success: false
});
});
}
}
}
效果如下
第二个 then 方法,不管 two.json 请求是成功还是失败,都会返回 value
第三个 then 方法里,第一个函数是 two.json 请求成功时调用,第一个函数是 two.json 请求失败时调用
三、catch
和 then 的第二个参数一样,用来指定 reject 的回调
案例4
demo/src/index.js
import $ from 'jquery';
const getJSON = (opts) => {
// ajax 需要的参数都可以写在 opts 里
console.log('opts', opts)
const {
url = '',
type = 'GET',
} = opts;
const promise = new Promise(function(resolve, reject) {
$.ajax({
url,
type,
success: (data = {}) => {
if (data.success) {
resolve(data);
} else {
reject(data);
}
},
error: (err = {}) => {
reject(err);
}
})
});
// 返回一个Promise对象
return promise;
}
getJSON({url: 'one.json'})
.then((value) => {
// one.json 请求成功拿到的结果
console.log('resolved', value);
})
.catch((reason) => {
// one.json 请求失败拿到的结果
console.log('rejected', reason);
});
效果如下
可以自己尝试下, 将 demo/webpack.config.js 里,one.json 请求的 success 改为 false ,就可以看到 rejected 的效果, 如下
demo/src/index.js
import $ from 'jquery';
const getJSON = (opts) => {
// ajax 需要的参数都可以写在 opts 里
console.log('opts', opts)
const {
url = '',
type = 'GET',
} = opts;
const promise = new Promise(function(resolve, reject) {
$.ajax({
url,
type,
success: (data = {}) => {
if (data.success) {
resolve(data);
} else {
reject(data);
}
},
error: (err = {}) => {
reject(err);
}
})
});
// 返回一个Promise对象
return promise;
}
getJSON({url: 'one.json'})
.then((value) => {
// one.json 请求成功拿到的结果
console.log('resolved', value);
// 这里 aaa 未定义
console.log(aaa);
})
.catch((reason) => {
console.log('rejected');
// one.json 请求失败拿到的结果
console.log('reason', reason);
});
效果如下
aaa 未定义,如果不写 catch 部分代码运行到这里就会报错, 不往下运行了,如下
写了 catch 后,这里就进入了 catch ,并且将报错原因也传到 reason 中去了
reason ReferenceError: aaa is not defined
这么写,即便是有错误的代码也不会报错,和 try/catch 相同。
一般来说,不要在 then 方法里面定义 Reject 状态的回调函数(即 then 的第二个参数),总是使用catch方法。
四、finally
finally方法用于指定不管 Promise 对象最后状态如何,都会执行的操作
案例5
demo/src/index.js
import $ from 'jquery';
const getJSON = (opts) => {
// ajax 需要的参数都可以写在 opts 里
console.log('opts', opts)
const {
url = '',
type = 'GET',
} = opts;
const promise = new Promise(function(resolve, reject) {
$.ajax({
url,
type,
success: (data = {}) => {
if (data.success) {
resolve(data);
} else {
reject(data);
}
},
error: (err = {}) => {
reject(err);
}
})
});
// 返回一个Promise对象
return promise;
}
getJSON({url: 'one.json'})
.then((value) => {
// one.json 请求成功拿到的结果
console.log('resolved', value);
})
.catch((reason) => {
// one.json 请求失败拿到的结果
console.log('rejected', reason);
})
.finally(() => {
console.log('finally')
});
效果如下
reject 的情况大家自己尝试
五、Promise.all()
提供了并行执行异步操作的能力,并且在所有异步操作执行完后才执行回调
案例6
demo/src/index.js
import $ from 'jquery';
const getJSON = (opts) => {
// ajax 需要的参数都可以写在 opts 里
console.log('opts', opts)
const {
url = '',
type = 'GET',
} = opts;
const promise = new Promise(function(resolve, reject) {
$.ajax({
url,
type,
success: (data = {}) => {
if (data.success) {
resolve(data);
} else {
reject(data);
}
},
error: (err = {}) => {
reject(err);
}
})
});
// 返回一个Promise对象
return promise;
}
// all接收一个数组参数
Promise
.all([getJSON({url: 'one.json'}), getJSON({url: 'two.json'})])
.then((data) => {
console.log('resolved', data);
})
.catch((err) => {
console.log('rejected', err);
})
.finally(() => {
console.log('finally')
});
all 接受一个数组作为参数,里面的值最终都算返回Promise对象
效果如下
当这两个请求都请求成功后,就进入 then 里,返回的结果是把两个请求的结果塞入一个数组中去了。
Promise.all() 可以并行执行多个异步操作,并且在一个回调函数中能拿到所有的返回结果。
当 one.json 和 two.json 的 success 都为 false (webpack.config.js里修改),结果如下
当 one.json 的 success 为 false, two.json 的 success 为 true ,结果如下
当 one.json 的 success 为 true, two.json 的 success 为 false ,结果如下
从这些可以得出,all里,只要有一个被 rejected,就会走到 catch 里去,catch 里 console 的值,是第一个被 rejected 的返回值
六、 Promise.race()
写法和 Promise.all() 类似,区别在于,race 里的异步操作,谁先完成,then 里的返回值就是谁的。
案例7
demo/src/index.js
import $ from 'jquery';
const getJSON = (opts) => {
// ajax 需要的参数都可以写在 opts 里
console.log('opts', opts)
const {
url = '',
type = 'GET',
} = opts;
const promise = new Promise(function(resolve, reject) {
$.ajax({
url,
type,
success: (data = {}) => {
if (data.success) {
resolve(data);
} else {
reject(data);
}
},
error: (err = {}) => {
reject(err);
}
})
});
// 返回一个Promise对象
return promise;
}
// all接收一个数组参数
Promise
.race([getJSON({url: 'one.json'}), getJSON({url: 'two.json'})])
.then((data) => {
console.log('resolved', data);
})
.catch((err) => {
console.log('rejected', err);
})
.finally(() => {
console.log('finally')
});
结果如下
one.json 先求情完毕,then 里的 console 打印出来的就是 one.json 返回的结果。
如果给 $.ajax 外层包一个 setTimeout ,且判断下如果是 one.json 的时候就延迟一秒,如下
setTimeout(()=>{
$.ajax({
url,
type,
success: (data = {}) => {
if (data.success) {
resolve(data);
} else {
reject(data);
}
},
error: (err = {}) => {
reject(err);
}
})
}, url === 'one.json' ? 1000 : 0)
效果如下
这里 two.json 先请求完毕,因此 then 里的 console 打印出来的就是 two.json 返回的结果。
(ps: 下面是不加 setTimeout 的三种情况)
当 one.json 和 two.json 的 success 都为 false (webpack.config.js里修改),结果如下
当 one.json 的 success 为 false, two.json 的 success 为 true ,结果如下
当 one.json 的 success 为 true, two.json 的 success 为 false ,结果如下
从这些可以得出,race 里:
-
最快完成的那个,如果走到 rejected,不管后面是 rejected 还是 resolved, 都会进入 catch , 且 catch 里 console 的值,就是最快被 rejected 的值
-
最快完成的那个,如果走到 resolved,不管后面是 rejected 还是 resolved,
都会进入 then , 且 then 里 console 的值,就是最快被 resolved 的值
上面的几种情况,如果你注意看 Network 的话,就会发现,不管 one.json 有没有执行成功,two.json 的请求都还是会发送
我们也可以通过在 ajax 里打印 console 来看,如下
$.ajax({
url,
type,
success: (data = {}) => {
if (data.success) {
resolve(data);
console.log('请求成功', url)
} else {
reject(data);
console.log('请求失败', url)
}
},
error: (err = {}) => {
reject(err);
console.log('请求失败', url)
}
})
效果如下
当 then 里的回调开始执行时,getJSON({url: 'two.json'}) 并没有停止,仍旧再执行。
七、Promise.resolve()
有时需要将现有对象转为 Promise 对象,Promise.resolve方法就起到这个作用
案例8
demo/src/index.js
const obj = {
a: 1,
b: 2
}
const promise = Promise.resolve(obj);
console.log(promise)
promise.then((v) => {
console.log(v)
})
结果如下
八、Promise.resolve()
Promise.reject(reason)方法也会返回一个新的 Promise 实例,该实例的状态为rejected
案例9
demo/src/index.js
const obj = {
a: 1,
b: 2
}
const promise = Promise.reject(obj);
console.log(promise)
promise.then(v => {
// do nothing
}, v => {
console.log(v)
})
promise.catch(v => {
console.log(v)
})
结果如下