1. Promise
- 一种异步编程的解决方案,通过一个回调,避免更多的回调
异步编程允许我们在执行一个长时间任务时,程序不需要等待,而是继续执行之后的代码,直到这些任务完成之后再来通知你,通常是以回调函数(callback)的形式,这种编程模式避免程序的阻塞。尤其适用于需要经常进行网络操作,数据库访问的应用。
使用setTimeout()让一个函数在指定事件后执行,如以下例子
setTimeout(function (){
console.log('执行了回调函数')
},3000)
console.log(111)
// 111
// 执行了回调函数
这种不阻塞后面任务执行的任务就叫做异步任务。
回调地狱:在回调函数中再嵌套回调函数的情况称为回调地狱(是实现代码顺序执行的一种操作方式)
setTimeout(function(){
console.log('日日复月月');
setTimeout(function(){
console.log('月月复年年');
setTimeout(function(){
console.log('年年复此生')
},1000)
},2000)
},3000)
// 日日复月月
// 月月复年年
// 年年复此生
这种回调函数中嵌套回调函数的情况就叫做回调地狱。回调地狱就是为是实现代码顺序执行而出现的一种操作,
(1)代码的可读性差、可维护性差
(2)代码的扩展性差
promise解决办法:
可以调用他的.then方法并传递一个回调函数,如果这个请求在未来成功完成,那么回调函数会被调起,请求的结果会以参数的形式传递进来。并且可以使用then链式结构将多个异步操作串联起来,代码向下方增长而非向右,大大提升了可读性
function fn(str){ //str='日日复月月'
//创建Promise对象
let p = new Promise(function(resolve,reject){
let flag = true;
setTimeout(function(){ //模拟异步调用
if(flag){ //模拟异步调用成功
resolve(str) //将str通过resolve传递出去 ---- resolve('日日复月月')
}else{ //模拟异步调用失败
reject("操作失败") //将失败的信息通过reject传递出去
}
})
})
return p;
}
fn('日日复月月').then((data)=>{ //.then接收resolve(str)传回来的信息
console.log(data); //data=('日日复月月')
return fn('月月复年年');
}).then((data)=>{
console.log(data); //data=('月月复年年')
return fn('年年复此生')
}).then((data)=>{
console.log(data) //data=('年年复此生')
}).catch((err)=>{
console.log(err)
})
// 日日复月月
// 月月复年年
// 年年复此生
代码来源:回调函数和回调地狱_Han_Zhou_Z的博客-CSDN博客_回调地狱是什么意思
- Promise几乎就是它的字面意思,代表一个承诺,承诺这个请求会在未来某个时刻返回数据,
async、await他们是基于Promise之上的语法糖,让异步操作更加简单明了,当需要操作异步函数时,使用关键字async关键字将函数标记为异步函数,异步函数返回值为Promise对象,await可以等待promise完成之后返回最终的结果,response就相当于服务器返回的响应数据了。
async function f(){
{
f(),
````````````````````````````````
async function f(){
const response = await fetch("http://...")
{
//其中fetch也是一个异步函数
f(),
那如何表示异步函数是否完成了呢,Promise 异步操作有三种状态:pending(进行中)、fulfilled(已成功)和 rejected(已失败)。除了异步操作的结果,任何其他操作都无法改变这个状态。promise的实例可以传入两个参数表示两个状态的回调函数,第一个是resolve,第二个是reject,在promise中我们使用resolved代表fulfilled,使用rejected表示fail。
const p2 = new Promise(function(resolve,reject){
resolve('success3');
reject('reject');
});
当异步运行成功时,自然会调用resolve获得最终数据,而失败时调用reject可以传递错误信息等。
1.5 async 和 promise 的写法对比
async function func(){
return 'hello world'
}
console.log(func())
let p = new Promise((resolve,reject)=>{
resolve('hello world')
})
console.log(p)
虽然最后的打印的结果是一样的。但是第二个代码Promise对象的状态已经从pending 变成 fulfilled了,如果在then中抛出一个错误的话,也不会经过错误捕捉操作。从一开始就要决定状态, 然后根据状态去选择执行下一步。使用promise对象,如果第一步没有完成状态的选择,那么不会去执行then中的方法。
如果使用async的话,如果你在then中不抛出错误的话,它会默认按照fulfilled状态一直执行then中的方法,不会固定为fulfilled状态。只要你某个then方法中抛出错误,那么就直接跳过执行catch方法。
所以相比之下还是 async要更方便一点。
2.Generator函数
Generator 函数是 ES6 提供的 一种异步编程解决方案,语法行为与传统函数完全不同。
Generator函数有两个特征:
- function命令与函数名 之间有一个
'*'
- 函数体内部使用
yield
语句定义不同的内部状态。
定义:
function* hello() {
yield "hello";
yield "world";
return "done";
}
let h = hello(); // 此时拿到的是一个Generator对象
当调用它的next时 ,才会进入它的方法体,遇到yield,直接返回语句内容 ,并停止执行后面的语句,第二次调用next时 ,会执行下一条yield
例如打印上面定义的 Generator函数变量
console.log(h.next()); //{value:"hello", done: false}
console.log(h.next()); //{value:"world", done: false}
console.log(h.next()); //{value:"done", done: true}
console.log(h.next()); //{value: undefined, done: true}
原文链接:https://blog.csdn.net/weixin_45525272/article/details/123047831
ES6新特性:Generator函数_杨 戬的博客-CSDN博客_es6 generate
通过执行结果我们可以看到,通过hello()返回的h对象,每调用一次next()方法返回一个对象,该对象包含了value值和done状态。
直到遇到return关键字或者函数执行完毕,这个时候返回的状态为ture,表示已经执行结束了。
应用场景
控制流管理:
上面Promise的例子将回调地狱的例子优化,把回调函数,改成了直线执行的形式,但是加入了大量 Promise 的语法。Generator 函数可以进一步改善代码运行流程。
function* longRunningTask(value1) {
try {
var value2 = yield step1(value1);
var value3 = yield step2(value2);
var value4 = yield step3(value3);
var value5 = yield step4(value4);
// Do something with value4
} catch (e) {
// Handle any error from step1 through step4
}
}
原文链接:https://blog.csdn.net/weixin_43964148/article/details/106878215
然后,使用一个函数,按次序自动执行所有步骤。
scheduler(longRunningTask(initialValue));
function scheduler(task) {
var taskObj = task.next(task.value);
// 如果Generator函数未结束,就继续调用
if (!taskObj.done) {
task.value = taskObj.value
scheduler(task);
}
}
控制流管理:保证代码的顺序直线进行,不方便在于不能自动执行,流管理 比promise更方便
3.Module(模块化)
模块化就是将js文件分解为各个功能不同的js文件,每一个js文件就是一个独立的模块,每一个模块就对应界面上的一个部分,但是模块化管理,很多的数据要进行共享,就需要遵守一个规范,现在的标准的规范就是ES6
在node.js中遵循的是CommonJS的模块化规范,其中:
- 导入其他模块中使用require()方法
- 模块对外共享成员使用module.exports对象
模块化的好处就是模块化和规范化,降低沟通的成本,方便各模块的相互调用; 比如像导航栏就可以单独弄成一个小的模块,这样就可以复用
在ES6模块化规范之前,还有其他的各种各样的模块,AMD和CMD等,但是这些小的规范都是局限的,而ES6模块化的出现就解决了杂乱,直接统一进行管理。
ES6模块化规范是浏览器端和服务器端 的通用的模块化开发规范。在其中定义了:
- 每一个js文件都是一个独立的模块
- 导入其他的模块的成员使用import关键字
- 向外共享模块成员使用export关键字
ES6模块化主要包含3中用法:
- 默认导出与默认导入
- 按需导出与按需导入
- 直接导入并执行模块中的代码