Promise
介绍:
- Promise 是ES6对异步编程一种解决方案。Promise就是一个对象容器,将异步操作(同步也可以但是没有必要这么做)保存在容器内部,当异步操作执行时就可以Promise获取当前异步操作的消息。这种模式会比传统回调更强大与合理。
- Promise 异步操作有三种状态:pending(进行中)、fulfilled(已成功) 和 rejected(已失败)。除了异步操作的结果,任何其他操作都无法改变这个状态。
- Promise 对象只有:从 pending 变为 fulfilled 和从 pending 变为 rejected 的状态改变。只要处于 fulfilled 和 rejected ,状态就不会再变了即 resolved(已定型)。
基本用法
概念: Promise就是一个构造函数用来生成Promise实例对象
语法: Promise构造函数在生成实例对象时接收一个函数作为参数,该函数接收两个参数(由js引擎所提供)
- 参数一 resolve 是一个函数它的作用是将Promise 的状态由 pending变为fulfilled (resolve),是一个异步操作执行成功时需要调用的方法
- 参数二 reject 是一个函数它的作用是将Promise 的状态由 pending变为reject,是一个异步操作执行失败时需要调用的方法
以上两个参数都可以将自身接收的参数传递出去
let p = new Promise(function (resolve, reject) {
let num = Math.random()
if (num > 0.5) {
resolve(num) // 成功调用resolve方法,将num传递出去
} else {
reject('失败了!') // 失败调用reject方法,将'失败了!'传递出去
}
})
prototype.then
概念:Promise实例对象生成完毕后可以使用分别指定resolve状态和reject状态的回调函数
语法: then 方法接收两个函数作为参数
- 参数一 Promise 执行成功时的回调函数,接收resolve传递的参数
- 参数二 Promise 执行失败时的回调 接收reject传递的参数
两个函数只会有一个被调用。
let p = new Promise(function (resolve, reject) {
let num = Math.random()
if (num > 0.5) {
resolve(num) // 成功调用resolve方法
} else {
reject('失败了!') // 调用reject方法
}
})
p.then(
res => console.log('成功!' + res), // resolve 的回调函数
err => console.error(err) // reject的回调函数
)
prototype.catch
概念:Promise实例对象生成完毕后,当状态 从 pending转换为rejected状态时,then方法第二个参数的别名。reject抛出的异常会被catch所捕获
语法:
p.then(
res => console.log('成功!' + res), // resolve 的回调函数
err => console.error(err) // reject的回调函数
)
// 就可以改写为
p.then(res => console.log('成功!' + res) // resolve 的回调函数
.catch(err => console.error(err)) // reject的回调函数
prototype.finally
概念:在promise结束时,无论结果是fulfilled或者是rejected,都会执行指定的回调函数。一般做一些异步请求的清理工作。
语法:
p.finally(() => console.log('异步操作执行完毕,无论成功失败') )
p.then(function(json) { })
.catch(function(error) { })
.finally(function() { });
all
概念:该方法接收一个数组包含多个promise对象并返回一个新的promise对象。
注意:当多个promise都成功完成时,all的状态也是成功。新的promise对象就会调用其resolve方法,根据数组中promise的顺序将对应的resolve返回值存入新promise的resolve数组中;如果有一个Promise失败 all的状态也是失败,失败的原因是第一个失败 promise 的结果。
语法:
function fakeAjax(timeout, name , isSuccess = true) {
return new Promise((resolve, reject) => {
setTimeout(() => {
if(isSuccess) {
resolve(name+'-data')
}else {
reject(name)
}
}, timeout)
})
}
let p1 = fakeAjax(2500, 'p1')
let p2 = fakeAjax(1500, 'p2')
let p3 = fakeAjax(800, 'p3')
// 所有Promise都成功才会进入then,
// then的成功回调函数中的参数是一个数组,包含all方法接收多个Promise对象数组成功返回值
Promise.all([p1, p2, p3])
.then(res => console.log(res)) // ['p1-data','p2-data','p3-data']
.catch(err => console.error(err) )
let p1 = fakeAjax(2500, 'p1')
let p2 = fakeAjax(1500, 'p2', false)
let p3 = fakeAjax(800, 'p3', false)
// 只要有任何一个Promise失败all就会进入catch
// catch 回调函数接收的参数是第一个失败Promise的返回值
Promise.all([p1, p2, p3])
.then(res => console.log(res))
.catch(err => console.error(err)) // 'p3'
any
概念:该方法接收多个 promise对象并返回一个 promise对象。多个 promise 只要有一个成功执行就会被调用promise对象方法
注意:如果有一个Promise成功 any就会返回成功,返回的值是第一个成功 promise resolve值。
语法:
function fakeAjax(timeout, name, isSuccess = true) {
return new Promise((resolve, reject) => {
setTimeout(() => {
console.log(name + '执行完毕')
if (isSuccess) {
resolve(name)
} else {
reject(name)
}
}, timeout)
})
}
let p1 = fakeAjax(2500, 'p1')
let p2 = fakeAjax(1500, 'p2')
let p3 = fakeAjax(800, 'p3')
Promise.any([p1, p2, p3])
.then(res => console.log(res)) // 'p3'
.catch(err => console.error(err))
注意:any方法还处于试验阶段,尚未被所有浏览器支持
race
概念:该方法接收多个 promise对象并返回一个 promise对象。 只要有一个完成(无论成功失败) 。返回promise对象就会调用完成Promise状态
语法:
let p1 = fakeAjax(2500, 'p1')
let p2 = fakeAjax(1500, 'p2')
let p3 = fakeAjax(800, 'p3', false)
Promise.race([p1, p2, p3])
.then(res => console.log(res))
.catch(err => console.error(err)) // 'p3'
let p1 = fakeAjax(2500, 'p1')
let p2 = fakeAjax(500, 'p2')
let p3 = fakeAjax(800, 'p3')
Promise.race([p1, p2, p3])
.then(res => console.log(res)) // 'p2'
.catch(err => console.error(err))
allSettled
概念:该方法接收多个 promise对象并返回一个 promise对象。必须所有的Promise都完成(无论成功/失败)该方法才会resolve一个数组,数组中包含接收所有Promise完成信息(status:成功 fulfilled、失败 rejected,value: 成功返回值,reason:失败原因 )
语法:
let p1 = fakeAjax(2500, 'p1', false)
let p2 = fakeAjax(1500, 'p2')
let p3 = fakeAjax(800, 'p3', false)
let p4 = fakeAjax(300, 'p4')
Promise.allSettled([p1, p2, p3, p4])
.then(res => console.log(res))
/*
res 的值为
[
{status: "rejected", reason: "p1"},
{status: "fulfilled", value: "p2"},
{status: "rejected", reason: "p3"},
{status: "fulfilled", value: "p4"},
]
*/
resolve
概念:该方法直接创建一个成功Promise
注意:
- 该方法可以接受一个普通数据类型,或者是一个 thenabel (拥有then方法的对象)
- 如果传入的是 thenabel对象 该对象的then属性会修改(类似于替换效果)掉当前promis的then方法
不要在thenable上调用自身Promise.resolve 会造成无限递归
语法:
// resolve普通数据
Promise.resolve(10086).then(res => console.log(res)) // 10086
// resolve promise对象 (promise对象拥有then属性所以属于thenable)
let demo = Promise.resolve(333)
let p = Promise.resolve(demo)
p.then(demo => console.log(demo)) // 333
// 等价于上面的代码 Promise.resolve(333).then 替换 p.then
// Promise.resolve(333).then(data => console.log(data))
// resolve 自己指定的thenable对象
let thenabel = {
then: function (func) {
console.log(1231231321321)
func('看得懂吗?')
}
}
let p = Promise.resolve(thenabel)
p.then(data => { console.log(data) })
// resolver 配合thenabel可以抛出异常
let thenabel = {
then: function () {
console.log('抛出异常')
throw new Error('异常')
}
}
let p = Promise.resolve(thenabel)
// p.then().catch(err => console.log(err))
p.then(function() {//该方法不执行}, err => console.error(err)) // 参数二执行等价于上面的写法
reject
概念:该方法直接创建一个失败Promise
语法:
Promise.reject(new Error('失败')).catch(error => console.log(error)) // "失败"
Async函数
概念: async 是 ES7 才有的与异步操作有关的关键字,他会将异步操作用同步代码的形式表现出来
语法:是一个使用async关键声明的函数。在函数内部配合await关键字将异步回调的代码改写成同步代码的格式
function demo () {
new Promise(function (resolve, reject) {
let num = Math.random()
if (num > 0.5) {
resolve(num) // 成功调用resolve方法
} else {
reject('失败了!') // 调用reject方法
}
})
.then(res => console.log('成功!' + res))
.catch(err => console.error(err) )
}
// 下面的代码等价于上面的代码
async function asyncDemo() {
try {
let res = await new Promise(function (resolve, reject) {
let num = Math.random()
if (num > 0.5) {
resolve(num) // 成功调用resolve方法
} else {
reject('失败了!') // 调用reject方法
}
})
console.log(res, 'async')
}
catch (err) {
console.log('Error:'+ err)
}
}
asyncDemo()
注意:
- async 函数的返回值,不是普通对象的返回值,而是Promise对象
async function test() {
return 'hello world'
}
// test() 等价于 new Promise((resolve) => resolve('hello world'))
console.log(test()) // Promise {<resolved>: "hello world"}
test().then(r => console.log(r)) // 'hello world'
案例 axios的使用(Axios 是一个基于 promise 的 HTTP 库,可以用在浏览器和 node.js 中)
<script src="https://unpkg.com/axios/dist/axios.min.js"></script>
<script>
// 异步函数
async function test() {
let res = await axios('http://musicapi.leanapp.cn/search?keywords=一')
console.log(res)
}
// 普通函数
function test() {
axios('http://musicapi.leanapp.cn/search?keywords=二')
.then(res => {
console.log(res,'12313')
})
}
</script>
「拓展」 generator
介绍: generator函数是ES6提供的一种生成器对象,语法上可以将它理解成,封装了多个内部状态的一个可迭代状态机。
语法: generator 是一个普通函数,调用与普通函数相同但是有几个不同特征:
- function关键字与函数名之间有个星号(新版本必须包含星号)
- 函数体内部使用yield表达式定义迭代状态
function* helloGenerator() {
console.log(0)
yield 'start';
console.log(1)
yield 'loading'
console.log(2)
return 'end';
}
- 函数调用时并不会执行,而是返回了一个遍历对象(指向内部状态的指针对象)
- 必须调用对象自身的next方法,使指针指向下一个yield声明的状态值。函数内部的代码就从当前位置向下执行到下一个yield/return的位置,并把下一个yield/return指定的值返回出去
console.log(a)
// 执行函数第一行代码到第一个yield代码
console.log(a.next()) // 0 {value: 'start', done: false}
// 执行函数第一个yield代码到第二个yield之间的代码
console.log(a.next()) // 1 {value: "loading", done: false}
// 执行函数第二个yield代码到return之间的代码
console.log(a.next()) // 2 {value: "end", done: true}
注意:
- yield不是必要的,若函数Generato没有yield调用next方法时就会,函数就会一直执行到结束
- yield 是惰性求值的,只会在next方法指定到该yield时才会计算自己的状态值
- Generator函数中若没有return,函数执行到最后会返回一个value为undefined的对象
方法:
- prototype.next() 返回一个由 yield表达式生成的值。该方法可以接受一个参数作为向生成器传递的值,即当前yield的返回值
function* gen() {
while (true) {
let value = yield null;
console.log(value);
}
}
let g = gen();
g.next(1);
// "{ value: null, done: false }"
g.next(2);
// 2
// "{ value: null, done: false }"
- prototype.return() 返回给定的值并结束生成器。该方法也可以接受一个参数效果与next相同
function* gen() {
yield 1;
yield 2;
yield 3;
}
let g = gen();
g.next(); // { value: 1, done: false }
g.return("foo"); // { value: "foo", done: true }
g.next(); // { value: undefined, done: true }
- prototype.throw() 向生成器抛出一个错误。使用 Error 的实例对调试非常有帮助.
function* gen() {
while(true) {
try {
yield 42;
} catch(e) {
console.log("Error caught!");
}
}
}
let g = gen();
g.next(); // { value: 42, done: false }
g.throw(new Error("Something went wrong")); // "Error caught!"
「拓展」 fetch
介绍:Fetch API 提供了一个 JavaScript 接口,用于访问和操纵 HTTP 技术的一些具体部分,例如请求和响应。它还提供了一个全局 fetch() 方法,该方法提供了一种简单,合理的方式来跨网络异步获取资源。
这种功能以前是使用 XMLHttpRequest 实现的。Fetch 提供了一个更理想的替代方案,可以很容易地被其他技术使用,例如 Service Workers。
注意,fetch 规范与 jQuery.ajax() 主要有三种方式的不同:
- 当接收到一个代表错误的 HTTP 状态码时,从 fetch() 返回的 Promise 不会被标记为 reject, 即使响应的 HTTP 状态码是 404 或 500。相反,它会将 Promise 状态标记为 resolve (但是会将 resolve 的返回值的 ok 属性设置为 false ),仅当网络故障时或请求被阻止时,才会标记为 reject。
- fetch() 可以接受跨域 cookies;你也可以使用 fetch() 建立起跨域会话。
- fetch 不会发送 cookies。除非你使用了credentials 的初始化选项。
一个基本的 fetch 请求设置起来很简单。看看下面的代码:
fetch('http://example.com/movies.json')
.then(function(response) {
return response.json();
})
.then(function(myJson) {
console.log(myJson);
});
这里我们通过网络获取一个 JSON 文件并将其打印到控制台。最简单的用法是只提供一个参数用来指明想 fetch() 到的资源路径,然后返回一个包含响应结果的promise(一个 Response 对象)。
当然它只是一个 HTTP 响应,而不是真的JSON。为了获取JSON的内容,我们需要使用 json() 方法。