一、回调函数
回调函数是一个函数做为一个参数传到另一个函数里了,在函数执行完成之后在执行。
function f1(callback) {
setTimeout(function(){
callback();
},1000)
}
function f2(){
console.log("f2执行");
}
f1(f2);
1、什么是回调函数
回调函数就是把一个函数当做一个参数传给另一个函数,做为参数的这个函数就是回调函数。
写一个向人打招呼的函数
如果向很多人打招呼可以采用map的方式
此时的greet()就是回调函数,person.map()利用一个函数做为参数,被称为高阶函数
回调函数做为高阶函数的参数,高阶函数调用回调函数来执行任务。
/**
* 1个人打招呼
* @param {} name
* @returns
*/
function greet(name) {
return `hello,${name}`;
}
console.log(greet('tom')); //hello,tom
/**
* 多人打招呼
*/
const persons = ['yom','dom','com','jack']
const message = persons.map(greet);
console.log(message); //[ 'hello,yom', 'hello,dom', 'hello,com', 'hello,jack' ]
2、自己编写回调函数
/**
* 自己编写回调函数
* @param {*} array
* @param {*} callback
* @returns
*/
function map(array, callback) {
const mapArray = [];
for (const item of array) {
mapArray.push(
callback(item)
)
}
return mapArray;
}
function greet1(name){
return `hello,${name}`;
}
const person = ['tom','dom'];
const message = map(person,greet1);
console.log(message); // [ 'hello,tom', 'hello,dom' ]
3、同步回调
同步回调会发生阻塞,高阶函数在回调函数执行完成后,才会继续执行
function map2(array, callback) {
console.log("map is start");
const mapArray = [];
for (const item of array) {
mapArray.push(
callback(item)
)
}
console.log("map completed");
return mapArray;
}
function greet2(name) {
console.log("greet in callback");
return `hello + ${name}`;
}
const person2 = ['tom'];
const message2 = map2(person2, greet2);
console.log(message2);
// map is start
// greet in callback
// map completed
// [ 'hello + tom' ]
4、js原生的同步回调
/**
* js原生回调replace
*/
const person4 = 'Cristina';
const replace = person4.replace(/./g, function (char) {
return char.toLowerCase() === 'i' ? '1' : char
})
console.log(person4); //Cristina
console.log(replace); //Cr1st1na
/**
* js原生回调reduce
*/
const person3 = ['Ana', 'Elena'];
const countStartingA = person3.reduce(
function callback(count, name) {
const startA = name[0].toLowerCase() === 'a';
return startA ? count + 1 : count;
}, 0
)
console.log(countStartingA); // 1
/**
* js原生回调find
*/
const person3 = ['Ana', 'Elena'];
const nameStartingA = person3.find(
function callback(name) {
return name[0].toLowerCase() === 'a';
}
)
console.log(nameStartingA); // Ana
/**
* js原生回调forEach
*/
const person3 = ['Ana', 'Elena'];
person3.forEach(
function callback(name) {
console.log(name); // Ana Elena
}
)
5、异步回调
是非阻塞的,无需等待回调函数的完成,高阶函数先完成其他事件再回调函数
/**
* 异步回调
*/
console.log('setTimeout start');
setTimeout(function later() {
console.log("later");
}, 2000);
console.log("setTimeout completed");
// setTimeout start
// setTimeout completed
// later
js异步回调
/**
* Dom的事件监听器
*/
const myButton = document.getElementById('myButton');
myButton.addEventListener('click', function handler() {
console.log("Button Clicked");
})
/**
* 异步回调计数器函数
*/
setTimeout(function later() {
console.log('2秒之后执行');
}, 2000)
setInterval(function repeat() {
console.log('每2秒执行一次');
}, 2000)
可以把异步函数用作异步回调
/**
* 异步函数,做回调函数
*/
async function fetchUserNames() { // 异步函数
const resp = await fetch('https://api.github.com/users?per_page=5');
const users = await resp.json();
const names = users.map(({ login }) => login);
console.log(names);
}
const button = document.getElementById('fetchUsersButton');
button.addEventListener('click', fetchUserNames); // 异步回调函数
思考💡setTimeout(callback,0) 执行 callback 时是同步还是异步的?(答案还有待思考)
/**
* 考题
*/
function callback(){
console.log("异步还是同步");
}
setTimeout(callback,0); // 异步回调,无需等待callback执行,可以先执行后面的任务
console.log("我先执行");
//我先执行
// 异步还是同步
二、发布订阅模式
通过pub/sub模式,我们可以在信息中心清楚的看到有多少信号来源,方便集中管理,更加方便模块化管理,但是如果整个项目都使用pub/sub模式的话,流程就变得不太清晰,数据的得到和数据的处理分开,对于后期维护也是一个大问题。
三、Promise
promise会一直处理等待状态,直到它所包装的异步调用返回/超时/结束
promise的状态分成2类:1.解决(resolve) 2.拒绝(rejected)
var p = new Promise(function (resolve, reject) {
setTimeout(function () {
let result = 100;
if (result === 50) {
resolve(50);
} else {
reject(result);
}
}, 1000)
})
p.then(function (data) {
console.log("成功之后返回的参数是" + data);
}).catch(function (data) {
console.log("失败之后返回的参数是" + data);
})
1. 什么是promise
1、含义
promise是一个构造函数,只要通过new一个promise来解决异步编程
2、状态
promise是承诺的意思,首先明确promise有2个状态,从pending到resolve,或者从pending到reject,一旦状态发生改变就不会在改变,这也是承诺的含义
3、基本用法
promise接受一个函数作为参数,函数的两个参数主要是resolve和reject
const promise = new Promise(function(resolve, reject) {
// ... some code
if (/* 异步操作成功 */){
resolve(value);
} else {
reject(error);
}
});
2. promise解决了什么问题
promise解决了回调地狱的问题
1、回调地狱代码
// 回调地狱
doSomething(function (result) {
doSomethingElse(result, function (newResult) {
doThirdThing(newResult, function (finalResult) {
console.log('最后结果' + finalResult);
}, failCallBack)
}, failCallBack)
}
2、promise代码
doSomething().then(function (result) {
return doSomethingElse(result)
})
.then(function (newResult) {
return doThirdThing(newResult)
})
.then(function (finalResult) {
console.log('最后结果' + finalResult);
})
.catch(failCallBack)
3、async代码
async function request() {
try {
const result = await doSomething();
const newResult = await doSomethingElse(result);
const finalResult = await doThirdThing(newResult);
console.log('最后结果' + finalResult);
} catch (error) {
failureCallback(error);
}
}
3. promise的方法
promise自己的方法,resolve、reject、all、race、any
promise原型方法,catch、then、finally
1、then
promise创建完成之后,使用then进行调用,then接受2个函数做参数,分别对于resolve的reject的去情况
promise.then(function(value) {
// success
}, function(error) {
// failure
});
2、catch
catch相当于then方法的第二个参数,但是catch还有一个作用就是执行resolve函数,如果抛出异常不会停止运行,会进入catch方法中
p.then((data) => {
console.log('resolved',data);
}).catch((err) => {
console.log('rejected',err);
});
3、all
执行机制: all 方法接受多个promise对象组成的数组做参数,所有promise的状态是reslove,all方法的状态就是reslove,有一个promise的状态是reject,all方法的状态就是reject(类似数组的every方法,全部定结果)
返回值: 成功返回的是数组,失败返回的是最先被reject的状态值
执行顺序: promise.all 返回的结果数组里面的顺序和promise.all 接收的数组参数的顺序是一致的,但是内部的执行顺序不是按照顺序的
使用场景: 有些时候我们做一个操作可能得同时需要不同的接口返回的数据,这时我们就可以使用Promise.all;
let promise1 = new Promise((resolve, reject) => {
setTimeout(() => {
console.log(22);
resolve(1);
}, 2000)
});
let promise2 = new Promise((resolve, reject) => {
setTimeout(() => {
console.log(11);
resolve(2);
}, 1000)
});
let promise3 = new Promise((resolve, reject) => {
setTimeout(() => {
console.log(33);
resolve(3);
}, 3000)
});
Promise.all([promise1, promise2, promise3]).then(res => {
console.log(res);
})
// 1秒后输出11 2秒后输出22 3秒后输出33 [1,2,3](这个是prmise.all的返回结果)
4、race
执行机制: race接受多个promise对象组成的数组做参数,当最先执行完的事件执行完之后,就直接返回该promise对象的值。如果第一个promise对象状态变成resolved,那自身的状态变成了resolved;反之第一个promise变成rejected,那自身状态就会变成rejected。(类似数组的some方法,一个定结果)
注意点:一个promise执行完成,只是代表promise.race有了返回结果,不会影响其他promise对象的执行,只是只有先完成的Promise才会被Promise.race后面的then处理。其它的Promise还是在执行的,只不过是不会进入到promise.race后面的then内。
返回值: 当最先执行的promise的值
使用场景: 有几个服务器都具有相同的接口,这个时候我们不知道哪个服务器快,就可以使用promise.race
let promise1 = new Promise((resolve, reject) => {
setTimeout(() => {
console.log(22);
reject(1);
}, 2000)
});
let promise2 = new Promise((resolve, reject) => {
setTimeout(() => {
console.log(11);
resolve(2);
}, 1000)
});
let promise3 = new Promise((resolve, reject) => {
setTimeout(() => {
console.log(33);
resolve(3);
}, 3000)
});
Promise.race([promise1, promise2, promise3]).then(res => {
console.log(res);
}, rej => {
console.log(rej)
})
// 1秒后输出11 2(这个是prmise.race的返回结果) 2秒后输出22 3秒后输出33
5、finally
无论promise的状态是什么,都会执行finally方法
promise
.then(result => {···})
.catch(error => {···})
.finally(() => {···});
下面是一个例子,服务器使用 Promise 处理请求,然后使用finally方法关掉服务器。
server.listen(port)
.then(function () {
// ...
})
.finally(server.stop);
finally本质是then方法的封装
promise
.finally(() => {
// 语句
});
// 等同于
promise
.then(
result => {
// 语句
return result;
},
error => {
// 语句
throw error;
}
);
上面代码中,如果不使用finally方法,同样的语句需要为成功和失败两种情况各写一次。有了finally方法,则只需要写一次。
6、any
返回机制: Promise.any接受一个可迭代的对象做为参数,(比如数组)只要其中的一个promise成功,就返回那个成功的promise,如果所有可迭代的对象中没有一个promise成功,就返回一个失败的promise。
应用场景: 从最快的服务器检索资源
//有一个成功,就返回成功
const promises = [
Promise.reject('error a'),
Promise.reject('error b'),
Promise.resolve('result'),
]
Promise.any(promises).then((value) => {
console.log("value" + value);
}).catch((err) => {
console.log("err" + err);
})
// 浏览器状态下返回的结果
// value result
//所有的失败,就返回失败
const promises = [
Promise.reject('error a'),
Promise.reject('error b'),
Promise.reject('error c'),
Promise.reject('error d'),
]
Promise.any(promises).then((value) => {
console.log("value" + value);
}).catch((err) => {
console.log('err:' + err); // err: AggregateError: All promises were rejected
console.log(err.message);// All promises were rejected
console.log(err.name); // AggregateError
console.log(err.errors);// (4) ["error a", "error b", "error c", "error d"]
})
4. then为什么可以链式调用
因为promise返回值还是一个promise实例,所以可以使用then链式调用
5. all、race、any对比
Promise.any VS Promise.all
Promise.all是所有的成功,才会返回成功,有一个失败,都返回失败
Promise.any是有一个成功,就会返回成功,所有的失败,才会返回失败
Promise.any VS Promise.race
Promise.any 关注Promise是否已经解决
Promise.race 关注Promise执行的状态,其中哪个最先完成就返回其状态,不管成功与否
四、genertor分析
1、genertor是什么
genertor是es6引入的新的数据类型,看着像一个函数,不过定义是由 function * xxxx(){}组成,并且可以利用yield返回多次
2、genertor的语法
function* genFn(x) {
y = yield x + 1;
console.log(y);
}
let g = genFn(1);
console.log(g.next()); // { value: 2, done: false }
console.log(g.next()); // { value: undefined, done: true }
第一步let g = genFn(1);调用genFn时,返回的是一个指针对象,此时并没有执行函数,只是简单的调用
第二步,采用next语法,会移动内部指针,指向第一个遇到yield的语句,就暂停执行,交出函数的执行权
第三步,再次采用next语法,会从上一次的暂停之后继续执行(就像保留了记忆一样)
next返回一个对象,包含value,done2个属性,value是yield语句后面表达式的值,done表示整个genertor函数是否执行完毕
function* genFn(x) {
y = yield x + 1;
return y;
}
let g = genFn(1);
console.log(g.next()); // { value: 2, done: false }
console.log(g.next(4)); // { value: 4, done: true }
console.log(g.next()); // { value: undefined, done: true }
- next(xxx),入参会被当做上一个异步任务返回的结果,被y接受并返回
3、genertor执行机制
genertor函数调用之后并没有执行函数,只是返回一个遍历器对象,必须调用遍历器对象的next的方法,才可以使指针指向下一个状态
直到遇到下一个yield语句或者return语句才结束
3、yield表达式
yield是暂停执行的标志
遇到yield就暂停执行,并且把yield后面表达式的值,作为返回对象的value值
下一次调用next(),在继续往下执行,直到遇到下一个yield表达式
如果没有遇到新的yield表达式,就一直运行到函数结束,直到return语句为止,并将return后面表达式的值作为返回对象的value值
如果没有return语句,则返回对象的value值是undefined
4、yield和return的区别
区别在与每次遇到yield表达式,函数会暂停执行,下一次在从该位置之后继续执行
return语句不具备记忆功能
每个函数里只能执行一次return语句,但是可以执行多次yield表达式
yield只能用在genertor里,用到其他地方会报错
5、next()
next()本身没有返回值,或者说总是返回undefined
next可以带一个参数,这个参数会被当做上一个yield表达式的返回值
在第一次使用next()传参是无效的,V8引擎会直接忽略第一次使用next()的参数,只用第二次传参才是有效的
6、genertor处理异步
function* genFn() {
var result = yield fetch('https://zhuanlan.zhihu.com/p/114374428');
console.log("result",result);
}
let g = genFn();
let result = g.next();
result.value.then(data => {
return data;
}).then(data => {
g.next(data);
})
五、async/await异步处理
async 返回一个promise对象
await 后面可以接受一个promise对象
异步成功
async function h() {
let data = await Promise.resolve(22);
console.log(data); // 22
return data;
}
console.log(h());
// Promise { <pending> }
{/* 22 */}
异步失败
async function c() {
try {
let data = await Promise.reject(22);
console.log(11); // 不会执行
} catch (e) {
console.log(222) // 输出 222
}
return 333
}
console.log(c());
1、async函数
async函数返回值为promise对象,async函数返回prmise的结果由函数的执行结果决定 既然是prmise对象,那么我们用then调用
async不会阻塞后面代码的执行,它内部所有的阻塞都被封装在一个 Promise 对象中异步执行。
async function fn1() {
return Promise.reject(2);
}
fn1().then(
value => { console.log('onResolved()', value) },
reason => { console.log('onRejected()', reason) } // onRejected() 2
)
2、await表达式
(1)如果表达式是promise对象,await返回的是promise成功的值
(2)如果表达式是其他的值,将此值作为await的返回值
await 必须写在 async 函数中, 但 async 函数中可以没有 await,如果 await 的 Promise 失败了, 就会抛出异常, 需要通过 try...catch 捕获处理。
await后面是普通类型
function fn2() {
return 2;
}
async function fn3() {
const value = await fn2();
console.log(value); // 2
}
fn3();
await后面是promise
function fn1() {
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve(1000)
}, 1000);
})
}
async function fn3(){
const value = await fn1(); // await 右侧是promise,得到的结果就是promise成功的值
console.log(value); // 1000
}
fn3();
function fn2() {
return new Promise((resolve, reject) => {
setTimeout(() => {
reject(1000)
}, 1000);
})
}
async function fn3() {
try {
const value = await fn2()
} catch (error) {
console.log('得到失败的结果', error)
}
}
fn3() // 得到失败的结果 1000
3、async/await 比promise更加优越
简洁干净,使用 async/await 能省去写多少行代码
错误处理,async/wait 能用相同的结构和好用的经典 try/catch 处理同步和异步错误
调试,async/await 的一个极大优势是它更容易调试,使用 async/await 则无需过多箭头函数,并且能像正常的同步调用一样直接跨过 await 调用
4、async解决什么问题
async为了解决promise的then方法的链式调用,已经最原始的回调函数产生的回调地狱的问题。
5、关于async非常有意思的面试题?
// 面试题
function getJSON() {
return new Promise((resolve, reject) => {
setTimeout(() => {
console.log(2);
resolve(2)
}, 2000)
})
}
async function testAsync() {
await getJSON()
console.log(3);
}
testAsync()
//2
//3
思考💡:将async await语句解析翻译为Promise?
分析一下await做的事情: (1)将写在await后面的代码放到async内部的promise里去执行 (2)将await下面的代码放到前一个promise的then中去执行
看代码了:
function getJSON() {
return new Promise((resolve, reject) => {
setTimeout(() => {
console.log(2);
resolve(2)
}, 2000)
})
}
function testAsync(){
return Promise.resolve().then(()=>{
return getJSON();
})
.then(()=>{
return console.log(3)
})
}
testAsync()
// 2
// 3