语法:function * 函数名(){}
是什么
Generator 函数是 ES6 提供的一种异步编程解决方案。内部可以看成一个状态机,返回迭代器对象,调用next方法进入下一个状态。yield表达式时暂停的标志
使用的场景
Symbol.iterator属性,从而使得该对象具有 Iterator 接口。generator函数可以用next方法来传参,该参数就会被当作上一个yield表达式的返回值。yield表达式本身没有返回值,返回undefined
所以第一次next传参是没用的,只有从第二次开始next传参才有用
Generator生成器函数如果有返回值 则是最后一次next的返回值{value:xxx:done:true}
function* gen() {
yield 1
yield 2
yield 3
}
const g = gen()
console.log(g.next()) // { value: 1, done: false }
console.log(g.next()) // { value: 2, done: false }
console.log(g.next()) // { value: 3, done: false }
console.log(g.next()) // { value: undefined, done: true }
// 有return值
function* gen() {
yield 1
yield 2
yield 3
return 4
}
const g = gen()
console.log(g.next()) // { value: 1, done: false }
console.log(g.next()) // { value: 2, done: false }
console.log(g.next()) // { value: 3, done: false }
console.log(g.next()) // { value: 4, done: true }
yield后面跟promise,函数立即执行function f1(val){
return Promise.resolve(val);
}
function f2(val){
return new Promise(resolve=>{
setTimeout(()=>{resolve(val)},1000);
})
}
function* gen() {
yield f1(1);
yield f2(2);
return 'ending';
}
const g = gen()
console.log(g.next()) // { value: Promise { 1 }, done: false }
console.log(g.next()) // { value: Promise { }, done: false }
console.log(g.next()) // { value: 3, done: true }
function f1(val){
return Promise.resolve(val);
}
function f2(val){
return new Promise(resolve=>{
setTimeout(()=>{resolve(val)},1000);
})
}
function* gen() {
console.log(yield f1(1)); //1
console.log(yield f2(2)); //2
console.log(yield 'xxx');//'xxx'
return 'ending';
}
function run(gen){
var g = gen();
function next(data){
var result = g.next(data); //{ value: Promise { 1 }, done: false }
if (result.done) return result.value; //执行完毕就可以返回
Promise.resolve(result.value).then(function(data){ //获取promise的执行结果
console.log(data); //1,2,xxx
next(data); //将1作为yieId f1()执行的结果
});
}
next();
}
run(gen);
集合概念有数组、对象、Map、Set,需要有一个统一的接口机制来处理所有不同的数据结构
是什么
迭代器iterator是一种接口,为不同的数据结构提供统一的访问机制
好处
for..of遍历操作原理是什么?
迭代器对象,有一个next方法,每次调用next方法都将返回一个结果。结果值是一个object {value:xxx,done},value表示具体的返回值, done 是布尔类型的,表示集合是否遍历完成。
内部会维护一个指针,用来指向当前集合的位置,每调用一次 next 方法,指针都会向后移动一个位置。
// 如果需要实现逆序:i初始化为items.length-1,依次i--
//[Symbol.iterator] = createIterator
function createIterator(items) {
var i = 0;
return {//迭代器对象,它具有一个 next 方法,该方法会返回一个对象,包含 value 和 done 两个属性
next: function () {
var done = i >= items.length;
var val = !done ? items[i++] : undefined;
return {
done: done,
value: val
}
}
}
}
//测试
var it = createIterator(['a', 'b', 'c']);
console.log(it.next());// {value: "a", done: false}
console.log(it.next());// {value: "b", done: false}
console.log(it.next());// {value: "c", done: false}
console.log(it.next());// "{ value: undefined, done: true }"
console.log(it.next());// "{ value: undefined, done: true }"
console.log(it.next());// "{ value: undefined, done: true }"
可迭代对象
[Symbol.iterator]的属性,也称为实现了Iterator接口[Symbol.iterator]的属性会返回一个函数createIterator函数,创造迭代器对象的方法[Symbol.iterator]返回的函数执行之后会返回一个迭代器对象[Symbol.iterator]函数返回的迭代器对象中有一个名称叫做next的方法{value: 10, done: false}使用场景
是什么
async函数是自带执行器的generator函数,是generator函数的语法糖,当函数执行遇到await时候, 函数会交出执行权。
有什么作用
async/await是回调地狱的最佳解决方案,以同步的方式去写异步代码。
async函数返回值的状态
async函数返回的Promise对象,必须等到内部所有await命令后面的 Promise 对象执行完,才会发生状态改变,除非遇到return语句或者抛出错误。
next()到下一个状态await可以看成是then的语法糖。yield命令后面只能是 Thunk 函数或 Promise 对象,不能是原始类型的值。function f1(val){
return Promise.resolve(val);
}
function f2(val){
return new Promise(resolve=>{
setTimeout(()=>{resolve(val)},1000);
})
}
//async函数写法
async function gen () => {
console.log(await f1(1)); //1
console.log(await f2(2)); //2
console.log(await 'xxx');//'xxx'
return 'ending';
}
//Genrator函数写法
function* gen() {
console.log(yield f1(1)); //1
console.log(yield f2(2)); //2
console.log(yield 'xxx');//'xxx'
return 'ending';
}
async 函数的实现原理,就是将 Generator 函数和自动执行器,包装在一个函数里。
async function fn(args){
//.....代码
}
//等价于
//将Generator函数 添加自动执行器 变成async函数
function fn(args){
return run(function*(){//run函数就是自动执行generator函数
//.....代码
})
})
}
//和之前的建议版本一样,增加了检测,next的参数从data变成了函数
function run(genF) {
return new Promise((resolve, reject) => {
const result = genF(); //获取迭代器对象
function getState(nextF) {
let obj;
try {
obj = nextF();//执行迭代器函数 获取value {value:xxx.done:xxx}
} catch(e) {
return reject(e);
}
if(obj.done) { //迭代器是否执行完毕
return resolve(obj.value);
}
Promise.resolve(obj.value).then(function(val){
getState(function(){ return result.next(val) });
}, function(e){
getState(function(){ return result.throw(e)});
});
}
getState(() => result.next()); //开始执行迭代器
});
}
async错误捕获方式
function f1(val){
return Promise.reject(val*2);
}
async function fn(){
try {
await f1(3);
} catch (error) {
console.log(error); //6
}
}
fn()
try catch为什么不可以捕获到异步代码?
try catch是同步代码在执行栈中,异步任务会被推进队列中,根据事件循环机制,会先将执行栈中的代码执行完毕再去执行队列中的任务。
是什么
promise是异步编程解决方案之一,从语法来说Promise是一个构造函数
好处
状态
捕获错误的方式
.then()的第二次参数.catch()方法首先需要知道几个问题
状态相关
构造器
then原理-实现链式调用
.then的回调函数决定.then指定回调时,考虑当前的状态,如果此时状态已经改变了说明是先改变状态后指定的回调,回调函数异步调用。如果此时状态还是pending状态,说明是先指定回调后改变状态,此时需要把回调函数存起来,待状态改变之后再调用.then 可以不传失败的回调,那么需要将异常传递下去。如果成功的回调不是函数,将value值传递下去。(catch只会处理失败,所以成功的要继续向后传递成功)| 类型 | 描述 |
|---|---|
| promise.then | 可以指定成功和失败的回调 |
| promise.catch | 用于指定失败的回调,是特殊的then方法promise.then(undefined,onReject) |
| promise.finally | 无论成功和失败都会调用,并且将值和状态原封不动的传递给后面的then |
Promise.prototype.then = function (onResolved,onRejected) {
const self =this;
//catch只会处理失败,所以成功的要继续向后传递成功
onResolved =typeof onResolved==='function'?onResolved:value => value;
//实现异常穿透
onRejected =typeof onRejected==='function'?onRejected:reason =>{throw reason};
}
.then的回调函数返回值rejected,reason为抛出的异常resolved,value为返回值 return new Promise((resolve,reject)=>{
//根据回调函数执行的结果修改返回的promise的状态
function handle(callback) {
try {
const result = callback(self.data);//放在里面,放在外面这个会异步执行,返回获取不到,需要知道结果是什么
if(result instanceof Promise){//情况3
result.then(
value => resolve(value), //如果这个执行说明返回的promise是成功的
reason=> reject(reason)//如果这个执行说明返回的promise是失败的
);//.then才知道promise是成功还是失败
}else {
resolve(result); //情况2
}
} catch (error) {
reject(error);//情况1
}
}
}
.then指定回调时,考虑当前的状态,如果此时状态已经改变了说明是先改变状态后指定的回调,回调函数异步调用。如果此时状态还是pending状态,说明是先指定回调后改变状态,此时需要把回调函数存起来,待状态改变之后再调用//如果先指定回调,需要将回调函数保存起来,回调函数执行完毕后还需要修改新promise对象的状态。这里没设置异步执行的原因是,回调的执行是在构造函数中,在构造函数中已经指定了是异步的了
if(self.status===PENDING){
self.callbacks.push({
onResolved(value){
handle(onResolved);
},onRejected(reason){
handle(onRejected);
}
});
}
//如果先改变状态,后指定回调,状态已经改变了,这里需要指定异步调用
else if(self.status===RESOLVED){
setTimeout(() => {
handle(onResolved);
});
}
else{//如果是reject
setTimeout(() => {
handle(onRejected);
});
}
总结
Promise.prototype.then = function (onResolved,onRejected) {
const self =this;
//catch只会处理失败,所以成功的要继续向后传递成功
onResolved =typeof onResolved==='function'?onResolved:value => value;
//实现异常穿透
onRejected =typeof onRejected==='function'?onRejected:reason =>{throw reason};
//返回一个新的Promise对象
return new Promise((resolve,reject)=>{
//根据回调函数执行的结果修改返回的promise的状态
function handle(callback) {
try {
const result = callback(self.data);//放在里面,放在外面这个会异步执行,返回获取不到,需要知道结果是什么
if(result instanceof Promise){//情况3
// result.then(
// value => resolve(value), //如果这个执行说明返回的promise是成功的
// reason=> reject(reason)//如果这个执行说明返回的promise是失败的
// );//.then才知道promise是成功还是失败
result.then(resolve,reject);//简洁写法
}else {
resolve(result); //情况2
}
} catch (error) {
reject(error);//情况1
}
}
//如果先指定回调,需要将回调函数保存起来,回调函数执行完毕后还需要修改新promise对象的状态。这里没设置异步执行的原因是,回调的执行是在构造函数中,在构造函数中已经指定了是异步的了
if(self.status===PENDING){
self.callbacks.push({
onResolved(value){
handle(onResolved);
},onRejected(reason){
handle(onRejected);
}
});
}
//如果先改变状态,后指定回调,状态已经改变了,这里需要指定异步调用
else if(self.status===RESOLVED){
setTimeout(() => {
handle(onResolved);
});
}
else{//如果是reject
setTimeout(() => {
handle(onRejected);
});
}
}
}
参数是callback函数,返回值是promise对象A,A的状态由finally前面的promise决定
无论成功和失败都会调用,并且将值和状态原封不动的传递给后面的then
1.需要获取前面promise执行的值,通过.then
2.需要执行callback函数,callback函数可能是一个异步函数,需要等待它执行完毕。通过Promise.resolve包装,就可以通过.then知道callback什么时候执行结束。
Promise.prototype.finally = function(callback){
let P = this.constructor
return this.then(
val => P.resolve(callback()).then(()=>val),//这返回的val是前面promise的结果,不是后面callback的结果
reason => P.resolve(callback()).then(()=>{throw reason})
)
}
//测试代码
new Promise((resolve, reject) => {
setTimeout(() => resolve("result"), 2000)
})
.finally(() => console.log("Promise ready"))
.then(result => console.log(result))
状态只能改变一次,当状态已经改变时,resolve和reject进入后立即返回,不会获取data,修改状态执行对应的回调
Promise.all(数组):返回一个Promise A
Promise.race(数组):返回一个promise,结果由第一个完成的promise结果决定。
Promise.allSettIed(数组):返回一个成功的promise,promise的值为数组,数组中包含每个promise执行的状态和返回值,当所有的promise状态都改变后,将数组返回。
Promise.all()
Promise.all = function(iterator){
let count = 0;
let res = new Array(iterator.length);
return new Promise((resolve,reject)=>{
iterator.forEach((element,index) => {
Promise.resolve(element).then(value=>{
count++;
res[index] = value;
if(count == iterator.length) resolve(res);
},reason=>{
reject(resolve)
})
});
})
}
const p1 = new Promise((resolve, reject) => {
resolve('成功了')
})
const p2 = Promise.resolve('success')
const p3 = Promise.reject('失败')
Promise.all([p1, p2]).then((result) => {
console.log(result) //["成功了", "success"]
}).catch((error) => {
//未被调用
})
Promise.all([p1, p3, p2]).then((result) => {
//未被调用
}).catch((error) => {
console.log(error) //"失败"
});
Promise.allSettled ()
Promise.allSettled = function(iterator){
let res = new Array(iterator.length);
let count = 0 ;
return new Promise((resolve,reject)=>{
const pushResult = (index,status,value)=>{
res[index]= {status:status,value:value};
count++;
if(count == iterator.length)resolve(res);
}
iterator.forEach((element,index) => {
Promise.resolve(element).then(value=>{
fn(index,'fulfilled',value);
},reason=>{
fn(index,'rejected',reason);
})
});
})
}
//提供两个模拟API
api = () =>{};
warning = ()=>{};
function timing(){
return new Promise((resolve,reject)=>{
setTimeout(()=>{
reject();
},5000)
})
}
function apiTiming(){
const arr = [api(),timing()];
Promise.race(arr).then(res=>{
console.log(res);
}),catch(e=>{
warnning(e);
})
}
实现一个具有并发数量限制的异步任务调度器,可以规定最大同时运行的任务。
JS实现一个带并发限制的异步调度器Scheduler,保证同时运行的任务最多有两个。完善下面代码的Scheduler类,使以下程序能够正常输出:
class Scheduler {
add(promiseCreator) { ... }
// ...
}
const timeout = time => new Promise(resolve => {
setTimeout(resolve, time);
})
const scheduler = new Scheduler(2);
const addTask = (time,order) => {
scheduler.add(() => timeout(time).then(()=>console.log(order))) //实现1: add方法参数不是一个promise 不用管返回值
scheduler.add(() => timeout(time)).then(()=>console.log(order)) //实现2: add方法参数是一个promise,返回一个promise
}
addTask(1000, '1');
addTask(500, '2');
addTask(300, '3');
addTask(400, '4');
// output: 2 3 1 4
分析
任务调度器-控制任务的执行,当资源不足时将任务加入等待队列,当资源足够时,将等待队列中的任务取出执行
在调度器中一般会有一个等待队列queue,存放当资源不够时等待执行的任务。
具有并发数据限制,假设通过max设置允许同时运行的任务,还需要count表示当前正在执行的任务数量。
当需要执行一个任务A时,先判断count==max 如果相等说明任务A不能执行,应该被阻塞,阻塞的任务放进queue中,等待任务调度器管理。
如果countcount++执行任务A,执行完毕后count--。
此时如果queue中有值,说明之前有任务因为并发数量限制而被阻塞,现在count
// output: 2 3 1 4
class Scheduler {
constructor(max) {
this.tasks = [], // 待运行的任务
this.count = 0 // 正在运行的任务
this.max = max;
}
// promiseCreator 是一个异步函数,return Promise
add(promiseCreator) {//add需要返回promise
return new Promise((resolve, reject) => {
promiseCreator.resolve = resolve //将resolve保存起来,等待promiseCreator执行后,在start中调用控制add返回
if (this.count < this.max) {
this.start(promiseCreator)
} else {
this.tasks.push(promiseCreator)
}
})
}
start(promiseCreator) {
this.count++;
promiseCreator().then(() => {
promiseCreator.resolve(); //add返回
this.count--;
if (this.tasks.length) {
this.start(this.tasks.shift())
}
})
}
}
const timeout = (time) => new Promise(resolve => {
setTimeout(resolve, time);
})
const scheduler = new Scheduler(2)
const addTask = (time, order) => {//() => new Promise(setTimeout(resolve, 1000))
scheduler.add(() => timeout(time)).then(() => console.log(order))
}
addTask(1000, '1');
addTask(500, '2');
addTask(300, '3');
addTask(400, '4');
async和await
class Scheduler {
constructor(max) {
this.tasks= []
this.count = 0;
this.max =max;
}
async add(promiseCreator) {
if(this.count >= this.max) await new Promise(resolve => this.tasks.push(resolve)); //将resolve放入队列中等待执行
this.count++
const res = await promiseCreator()
this.count--
if(this.tasks.length) this.tasks.shift()()
return res
}
}
const timeout = (time) => new Promise(resolve => {
setTimeout(resolve, time);
})
const scheduler = new Scheduler(2)
const addTask = (time, order) => {//() => new Promise(setTimeout(resolve, 1000))
scheduler.add(() => timeout(time)).then(() => console.log(order))
}
addTask(1000, '1');
addTask(500, '2');
addTask(300, '3');
addTask(400, '4');
同步任务
1.addTask(1000, ‘1’)执行到const res = await promiseCreator()的时候,交出执行权,执行下一步
2.addTask(500, ‘2’)同1
3.addTask(300, ‘3’)此时count = 2 所以执行到await new Promise(resolve => this.tasks.push(resolve))的时候交出执行权,执行下一步
4.addTask(400, ‘4’)同3
异步任务
1.time=500的定时器resolve, addTask(500, ‘2’)函数重获执行权,开始执行其中的后续代码:
this.count-- //count = 1
if(this.tasks.length) this.tasks.shift()() //使waitQueue中的第一个异步任务resolve,addTask(300, '3')重获执行权
return res //遇到return,则async函数返回的Promise的状态resolved, 所以.then(() => console.log(time, order))输出 500 '2'
2.addTask(300, ‘3’)函数重获执行权,执行到const res = await promiseCreator()交出执行权
3.time=300的定时器resolve, 后续流程同5
4.addTask(400, ‘4’)函数重获执行权, 后续同6
5.time=1000的定时器resolve, 后续流程同5
6.time=400的定时器resolve, 后续流程同5