Promise与async/await与Generator
Promise是什么:
Promise是异步微任务(process.nextTick、Promise.then() catch() finally()等)
用于解决异步多层嵌套回调的问题(回调地狱--小球运动),让代码的可读性更高、更容易维护
小球运动:一个小球元素使其按照右下左上的路径运动,点击后触发回调函数,如果不使用Promise那么就要层层嵌套回调函数
运动到右后 再到下 再到左 再到上,而此时想要修改运动路径的话,修改这一层又一层的代码会非常繁琐,而Promise的then()链就可以很好的解决这个问题
Promise使用:
Promise是ES6提供的一个构造函数,可以使用Promise构造函数new出一个实例,Promise构造函数接受一个函数作为参数,这个函数有两个参数,分别是resolve和reject;
resolve将Promise的状态由等待变为成功(resolved),将异步操作的结果作为参数传递出去;
reject将Promise的状态变为失败(rejected),在异步操作失败时调用,将异步操作报错的错误作为参数传递过去。
实例创建完成后,可以使用then方法分别指定成功或者失败的回调回调函数,也可以使用catch捕获失败,then和catch最终返回的也是一个Promise,所以可以链式调用
Promise的特点:
1.对象状态不受外界影响(Promise对象代表一个异步操作,有三种状态) - pending(等待状态) - resolved(成功状态) - rejected(失败状态)
2.一旦状态改变,就不会再变化,任何时候都可以得到这个结果Promise对象状态的改变只有两种可能,
pending => resolved (then第一个回调)和 pending => rejected(then第二个回调)
(这两个状态为结束状态,表示Promise的生命周期已结束)
3.resolve方法中的参数是then中回调函数的参数,reject方法中的参数是catch中的参数
4.then方法和catch方法返回的都是成功状态的Promise(catch中throw返回失败状态的Promise)
Promise的其他方法:
Promise.finally():当promise状态发生变化时执行(任何变化都执行),不变化不执行
Promise.resolve(value):返回成功状态的Promise对象,并将value转递给对应的then方法
(当resolve函数接收的是promise对象时,后面的then会根据传递的promise对象的状态变化决定执行哪一个回调)
Promise.reject():返回一个失败状态的Promise对象,并将给定的失败信息传递给对应的处理方法
Promise.any():接受一个promise对象集合,当其中的第一个promise成功时,就返回那个成功的promise值,不会等待其他promise全部完成
Promise.all():返回一个新的promise对象,该promise对象在参数对象里接收多个promise对象,参数中的promise都成功时才会触发成功,任意一个promise对象失败都会触发失败
Promise.race():使用第一个返回的promise实例对象,成功就是成功,失败就是失败
Promise.allSettled():该方法的状态无传入promise的状态无关,它永远都是成功的,只会记录下各个promise的表现
(Promise.all、Promise.race、Promise.allSettled传入的若不是promise数组,会将其转换成promise数组,任何可遍历对象都可作为数组)
async/await:
async/await是基于Promise实现的,使得异步代码看起来像同步代码,是写异步代码的新方式
async/await实际上是Generator的语法糖。顾名思义,async关键字,代表后面的函数中有异步操作,await表示等待一个异步方法执行完成
声明异步函数只要在普通函数前加上一个关键字async即可
async函数返回一个Promise对象,(若返回值不是Promise对象也会通过Promise.resolve() 封装成 Promise 对象返回)因此async函数return返回的值可以通过then方法来接收
async function funcA() {
return 'hello!';
}
funcA().then(value => {
console.log(value);
})
// hello!
await就是异步等待,等待的是一个Promise,因此await后面应该是一个Promise对象,若不是,也会被转成立即resolve的Promise
async函数被调用后就会开始执行,遇到await后就会等待其后面的异步操作执行完成,接着执行函数体后面的语句
总的来说:async函数调用不会造成代码的阻塞,但是await会造成async函数内部代码的阻塞
async function func() { console.log('async function is running!'); const num1 = await 200; console.log(`num1 is ${num1}`); const num2 = await num1+ 100; console.log(`num2 is ${num2}`); const num3 = await num2 + 100; console.log(`num3 is ${num3}`); } func(); console.log('run me before await!'); // async function is running! // run me before await! // num1 is 200 // num2 is 300 // num3 is 400
func函数执行后先输出了 ‘async function is running!’,接着遇到了await异步等待,函数返回执行后面的同步任务 'run me before await!'
同步执行完成后接着await等待的位置继续执行。
可以说:async函数可以看作多个异步任务包装成一个Promise对象,而await命令就是其内部的语法糖
await后面的Promise对象不会总是返回resolved状态,只要一个await后面的Promsie状态变成rejected,整个async都会中断执行
为了避免此情况可以使用try...catch来封装多个await:
async function func() { try { const num1 = await 200; console.log(`num1 is ${num1}`); const num2 = await Promise.reject('num2 is wrong!'); console.log(`num2 is ${num2}`); const num3 = await num2 + 100; console.log(`num3 is ${num3}`); } catch (error) { console.log(error); } } func(); // num1 is 200 // 出错了 // num2 is wrong!
async/await:使得异步代码看起来像同步代码
function sayHi(name) { return new Promise((resolved, rejected) => { setTimeout(() => { resolved(name); }, 2000) }) } async function sayHi_async(name) { const sayHi_1 = await sayHi(name) console.log(`你好, ${sayHi_1}`) const sayHi_2 = await sayHi('李四') console.log(`你好, ${sayHi_2}`) const sayHi_3 = await sayHi('王二麻子') console.log(`你好, ${sayHi_3}`) } sayHi_async('张三') // 你好, 张三 // 你好, 李四 // 你好, 王二麻子
Generator:
generator(生成器)是ES6标准引入的新的数据类型。一个generator看起来像个函数,但可以返回多次。
ES6定义的generator是借鉴了python中的generator概念和语法
函数的概念:一个函数是一段完整的代码,调用一个函数就是传入函数,然后返回结果
函数在执行的过程中,如果没有遇到return语句(没有return,就是隐含return undefined),控制权无法交回给被调用的代码
function foo(x) { return x + x; } var r = foo(1); // 调用foo函数
而generator定义如下:
function* foo(x) { yield x + 1; yield x + 2; return x + 3; }
generator由 function* 定义,并且除了return语句外可以通过 yield 返回多次
举个例子:斐波那契数列
function* fib(max) { var t, a = 0, b = 1, n = 0; while (n < max) { yield a; [a, b] = [b, a + b]; n ++; } return; }
调用generator和函数不同,有两种调用方法,一是调用generator的 next() 方法:
var f = fib(5); f.next(); // {value: 0, done: false} f.next(); // {value: 1, done: false} f.next(); // {value: 1, done: false} f.next(); // {value: 2, done: false} f.next(); // {value: 3, done: false} f.next(); // {value: undefined, done: true}
next()方法会执行generator代码,每次遇到 yield a;就会返回一个对象{value: a, done: false}。每次返回的value就是yield的返回值,done表示这个generator是否已经执行结束
二是直接使用 for...of 循环迭代generator
for (var x of fib(10)) { console.log(x); // 依次输出0, 1, 1, 2, 3, ... }
generator的用处:
由于generator在执行的过程中可以返回多次,所以他看上去像一个可以记住执行状态的函数,利用这一点,通过generator可以实现需要用面向对象才能实现的功能
generator还可以把异步代码变成 “同步” 代码
try { r1 = yield ajax('http://url-1', data1); r2 = yield ajax('http://url-2', data2); r3 = yield ajax('http://url-3', data3); success(r3); } catch (err) { handle(err); }
new关键字的执行过程:(构造函数为Func)
- 创建一个空对象{},并将该空对象继承Func.prototype(或为新对象添加属性__proto__,将该属性连接至构造函数原型)
- 执行构造函数,并将this指向刚刚创建的新对象
- 返回新对象
- new关键字后面的构造函数不能是箭头函数
参考:
https://segmentfault.com/a/1190000015488033
https://www.liaoxuefeng.com/wiki/1022910821149312/1023024381818112#0