Promise
ECMAscript 6 原生提供了 Promise 对象。
Promise 对象代表了未来将要发生的事件,用来传递异步操作的消息。
Promise 对象有以下两个特点:
1、对象的状态不受外界影响。Promise 对象代表一个异步操作,有三种状态:
- pending: 初始状态。
- fulfilled: 意味着操作成功完成。
- rejected: 意味着操作失败。
只有异步操作的结果,可以决定当前是哪一种状态,任何其他操作都无法改变这个状态。这也是 Promise 这个名字的由来,它的英语意思就是「承诺」,表示其他手段无法改变。
2、一旦状态改变,就不会再变,任何时候都可以得到这个结果。
Promise 对象的状态改变,只有两种可能:从 Pending 变为 Resolved 和从 Pending 变为 Rejected。只要这两种情况发生,状态就凝固了,不会再变了,会一直保持这个结果。就算改变已经发生了,你再对 Promise 对象添加回调函数,也会立即得到这个结果。这与事件(Event)完全不同,事件的特点是,如果你错过了它,再去监听,是得不到结果的。
Promise的使用
1.then基本使用
场景:请求网络接口,对成功和失败结果做不同处理
// 基本使用
new Promise((resolve, reject) => {
console.log('task start')
// 模拟请求网络数据
setTimeout(() => {
resolve('{"name": "james"}') // 成功
// reject(new Error('network error!')) // 失败
}, 1000);
}).then(value => {
// 处理成功的回调
console.log('success:' + value)
}, error => {
// 处理失败的回调
console.log('fail:' + error)
})
成功:
task start
success:{"name": "james"}
失败:
task start
fail:Error: network error!
2.then的多次调用
场景:例如一个页面需要请求一个接口,请求成功后需要:
1).将数据展示到页面
2).数据上报
那么可以这样实现:
// 2.then的多次调用
let p1 = new Promise((resolve, reject) => {
console.log('task start')
// 模拟请求网络数据
setTimeout(() => {
//resolve('{"name": "james"}') // 成功
reject(new Error('network error!')) // 失败
}, 1000);
})
// 1.显示到页面
p1.then(value => {
console.log('to show success:' + value)
}, error => {
console.log('to show fail1:' + error)
})
// 2.数据上报
p1.then(value => {
console.log('to report success2:' + value)
}, error => {
console.log('to report fail2:' + error)
})
成功:
task start
to show success:{"name": "james"}
to report success2:{"name": "james"}
失败:
task start
to show fail1:Error: network error!
to report fail2:Error: network error!
3.then的链式调用
场景:实际开发中,我们肯定需要对网络数据做处理,例如解密,json/xml解析,过滤等等操作,demo中是以获取json数据中name的字段,并将首字母大写
new Promise((resolve, reject) => {
console.log('task start')
// 模拟请求网络数据
setTimeout(() => {
resolve('{"name": "james"}') // 成功
reject(new Error('network error!')) // 失败
}, 1000);
}).then(rawData => {
return JSON.parse(rawData)
}).then(jsonObj => {
return jsonObj.name
}).then(oriName => {
return oriName.replace(oriName[0], oriName[0].toUpperCase())
}).then(name => {
console.log(name)
})
输出:
task start
James
4.catch的使用
4.1捕获reject失败
new Promise((resolve, reject) => {
console.log('task start')
// 模拟请求网络数据
setTimeout(() => {
//resolve('{"name": "james"}') // 成功
reject(new Error('network error!')) // 失败
}, 1000);
}).then(value => {
// 处理成功的回调
console.log('success:' + value)
}).catch(error => {
// 处理失败的回调
console.log('fail:' + error)
})
// 输出:
task start
fail:Error: network error!
4.2捕获执行器异常
new Promise((resolve, reject) => {
console.log('task start')
throw new Error('executor error')
// 模拟请求网络数据
setTimeout(() => {
//resolve('{"name": "james"}') // 成功
reject(new Error('network error!')) // 失败
}, 1000);
}).then(value => {
// 处理成功的回调
console.log('success:' + value)
}).catch(error => {
// 处理失败的回调
console.log('fail:' + error)
})
// 输出:
task start
fail:Error: executor error
4.3捕获then异常
new Promise((resolve, reject) => {
console.log('task start')
// 模拟请求网络数据
setTimeout(() => {
resolve('{"name": "james"}') // 成功
//reject(new Error('network error!')) // 失败
}, 1000);
}).then(value => {
// 处理成功的回调
throw new Error('then error')
console.log('success:' + value)
}).catch(error => {
// 处理失败的回调
console.log('fail:' + error)
})
// 输出:
task start
fail:Error: then error
5.finally的使用
不管promise最终的状态如何,finally传递的回调始终会执行
new Promise((resolve, reject) => {
console.log('task start')
//throw new Error('executor error')
// 模拟请求网络数据
setTimeout(() => {
resolve('{"name": "james"}') // 成功
//reject(new Error('network error!')) // 失败
}, 1000);
}).then(value => {
// 处理成功的回调
//throw new Error('then error')
console.log('success:' + value)
}).catch(error => {
// 处理失败的回调
console.log('fail:' + error)
}).finally(() => {
console.log('task end')
})
正常输出:
task start
success:{"name": "james"}
task end
reject失败输出:
task start
fail:Error: network error!
task end
执行器异常输出:
task start
fail:Error: executor error
task end
then异常输出:
task start
fail:Error: then error
task end
6.Promise.all的使用
all接收一个数组,数组里的元素可以是普通值,也可以是promise对象,普通对象直接返回,promise对象则等待其最终状态确定后返回。
数组里的promise都成功的情况,最终结果就是成功
function timeConsole(msg) {
let date = new Date()
console.log(date.toTimeString() + "->" + msg)
}
delayResolve = function(delay, message) {
return new Promise(function (resolve, reject) {
timeConsole(message + ' start')
setTimeout(function () {
timeConsole(message + ' success');
resolve(message);
}, delay);
});
},
delayReject = function(delay, message) {
return new Promise(function (resolve, reject) {
timeConsole(message + ' start')
setTimeout(function () {
timeConsole(message + ' fail');
reject(message);
}, delay);
});
}
Promise.all(['task1', delayResolve(2000, "task2"), delayResolve(1000, "task3"), 'task4'])
.then(function(v) {
timeConsole('all end:' + v)
}).catch(function(e){
timeConsole("catched:" + e);
}).finally(function() {
timeConsole("finally");
});
输出:
14:28:36 GMT+0800 (GMT+08:00)->task2 start
14:28:36 GMT+0800 (GMT+08:00)->task3 start
14:28:37 GMT+0800 (GMT+08:00)->task3 success
14:28:38 GMT+0800 (GMT+08:00)->task2 success
14:28:38 GMT+0800 (GMT+08:00)->all end:task1,task2,task3,task4
14:28:38 GMT+0800 (GMT+08:00)->finally
数组中的promise只要有一个失败,则最终结果就是失败
Promise.all(['task1', delayReject(2000, "task2"), delayResolve(1000, "task3"), 'task4'])
.then(function(v) {
timeConsole('all end:' + v)
}).catch(function(e){
timeConsole("catched:" + e);
}).finally(function() {
timeConsole("finally");
});
14:36:21 GMT+0800 (GMT+08:00)->task2 start
14:36:21 GMT+0800 (GMT+08:00)->task3 start
14:36:22 GMT+0800 (GMT+08:00)->task3 success
14:36:23 GMT+0800 (GMT+08:00)->task2 fail
14:36:23 GMT+0800 (GMT+08:00)->catched:task2 error
14:36:23 GMT+0800 (GMT+08:00)->finally
7.Promise.race的使用
race也接收一个数组,race最终的结果取决于最先结束的promise的结果
Promise.race([delayReject(2000, "task2"), delayResolve(1000, "task3")])
.then(function(v) {
timeConsole('all end:' + v)
}).catch(function(e){
timeConsole("catched:" + e);
}).finally(function() {
timeConsole("finally");
});
成功
14:48:19 GMT+0800 (GMT+08:00)->task2 start
14:48:19 GMT+0800 (GMT+08:00)->task3 start
14:48:20 GMT+0800 (GMT+08:00)->task3 success
14:48:20 GMT+0800 (GMT+08:00)->all end:task3
14:48:20 GMT+0800 (GMT+08:00)->finally
14:48:21 GMT+0800 (GMT+08:00)->task2 fail
失败
14:51:56 GMT+0800 (GMT+08:00)->task2 start
14:51:56 GMT+0800 (GMT+08:00)->task3 start
14:51:57 GMT+0800 (GMT+08:00)->task2 fail
14:51:57 GMT+0800 (GMT+08:00)->catched:task2 error
14:51:57 GMT+0800 (GMT+08:00)->finally
14:51:58 GMT+0800 (GMT+08:00)->task3 success
Generator
ES6 新引入了 Generator 函数,可以通过 yield 关键字,把函数的执行流挂起,为改变执行流程提供了可能,从而为异步编程提供解决方案。
Generator 有两个区分于普通函数的部分:
- 一是在 function 后面,函数名之前有个
*
- 函数内部有
yield
表达式。
其中 * 用来表示函数为 Generator 函数,yield 用来定义函数内部的状态。
function timeConsole(msg) {
let date = new Date()
console.log(date.toTimeString() + "->" + msg)
}
function * gen() {
timeConsole('before 1')
yield '1'
timeConsole('after 1')
timeConsole('before 2')
yield '2'
timeConsole('after 2')
timeConsole('before 3')
yield '3'
timeConsole('after 3')
}
let g = gen()
setTimeout(() => {
timeConsole(g.next().value)
}, 0);
setTimeout(() => {
timeConsole(g.next().value)
}, 1000);
setTimeout(() => {
timeConsole(g.next().value)
}, 2000);
setTimeout(() => {
timeConsole(g.next().value)
}, 3000);
输出
16:28:25 GMT+0800 (GMT+08:00)->before 1
16:28:25 GMT+0800 (GMT+08:00)->1
16:28:26 GMT+0800 (GMT+08:00)->after 1
16:28:26 GMT+0800 (GMT+08:00)->before 2
16:28:26 GMT+0800 (GMT+08:00)->2
16:28:27 GMT+0800 (GMT+08:00)->after 2
16:28:27 GMT+0800 (GMT+08:00)->before 3
16:28:27 GMT+0800 (GMT+08:00)->3
16:28:28 GMT+0800 (GMT+08:00)->after 3
16:28:28 GMT+0800 (GMT+08:00)->undefined
使用next()传入参数,yield接收参数
function * gen() {
timeConsole('before 1')
let r1 = yield '1'
timeConsole(r1)
timeConsole('before 2')
let r2 = yield '2'
timeConsole(r2)
timeConsole('before 3')
let r3 = yield '3'
timeConsole(r3)
}
let g = gen()
setTimeout(() => {
timeConsole(g.next('start').value)
}, 0);
setTimeout(() => {
timeConsole(g.next('after 1').value)
}, 1000);
setTimeout(() => {
timeConsole(g.next('after 2').value)
}, 2000);
setTimeout(() => {
timeConsole(g.next('after 3').value)
}, 3000);
输出
16:34:15 GMT+0800 (GMT+08:00)->before 1
16:34:15 GMT+0800 (GMT+08:00)->1
16:34:16 GMT+0800 (GMT+08:00)->after 1
16:34:16 GMT+0800 (GMT+08:00)->before 2
16:34:16 GMT+0800 (GMT+08:00)->2
16:34:17 GMT+0800 (GMT+08:00)->after 2
16:34:17 GMT+0800 (GMT+08:00)->before 3
16:34:17 GMT+0800 (GMT+08:00)->3
16:34:18 GMT+0800 (GMT+08:00)->after 3
16:34:18 GMT+0800 (GMT+08:00)->undefined
async/await
async/await 是 ES7 才有的与异步操作有关的关键字,和 Promise , Generator 有很大关联的,使用async/await在处理异步操作时代码可读性会更好
async
async 函数返回一个 Promise 对象,如果不是Promise对象,则自动转换成Promise对象,可以使用 then 方法添加回调函数。
async function myTask(p) {
return 'task result'
}
let result = myTask()
console.log(result)
result.then(value => console.log(value))
// 输出
// Promise { 'task result' }
// task result
await
await目前只能在异步函数 async function 内部使用,表示等待某表达式的执行结果
await针对所跟不同表达式的处理方式:
- Promise 对象:await 会暂停执行,等待 Promise 对象 resolve,然后恢复 async 函数的执行并返回解析值。
- 非 Promise 对象:直接返回对应的值。
function task1() {
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve('task1 end')
}, 3000);
})
}
function task2() {
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve('task2 end')
}, 2000);
})
}
function task3() {
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve('task3 end')
}, 1000);
})
}
async function myTask() {
let r0 = await 'task start'
timeConsole(r0)
let r1 = await task1()
timeConsole(r1)
let r2 = await task2()
timeConsole(r2)
let r3 = await task3()
timeConsole(r3)
}
myTask()
输出
17:12:12 GMT+0800 (GMT+08:00)->task start
17:12:15 GMT+0800 (GMT+08:00)->task1 end
17:12:17 GMT+0800 (GMT+08:00)->task2 end
17:12:18 GMT+0800 (GMT+08:00)->task3 end