js中callback,回调地狱,Promise和async,await的浅解

什么是callback?什么是回调地狱?怎么解决回调地狱?ES6中Promise的理解?async和await的理解?

一、 callback

回调只是使用JavaScript函数的惯例的名称。 JavaScript语言中没有特别的东西叫做“回调”,它只是一个约定。
不像大多数函数那样立即返回一些结果,使用回调函数需要一些时间来产生结果。
“异步”这个词,又名“异步”,意思是“需要一些时间”或“将来会发生,而不是现在”。
通常回调仅在进行I / O时使用,例如下载东西,阅读文件,与数据库交互等。

1.场景

在异步外面获取异步里面的结果时使用

2.使用回调函数拿取异步结果代码如下:
function a (fn){
	setTimeout(()=>{
		sum = 10
		//技巧:在有异步结果的位置,调形参函数,传结果
		fn(sum)	
	},0)
}
a((arr)=>{
	console.log(arr)// 10
})
// 简化抽离
function b(arr){
console.log(arr)// 10 
}
a(b)
3. 优点

在异步有结果的位置,调形参函数,传递结果
解决了同步的问题(只要有一个任务耗时很长,后面的任务都必须排队等着,会拖延整个程序的执行。)

4. 缺点
//1. 回调函数(callback)
setTimeout(() => {
    // callback 函数体
}, 1000)

ajax('XXX1', () => {
    // callback 函数体
    ajax('XXX2', () => {
        // callback 函数体
        ajax('XXX3', () => {
            // callback 函数体
        })
    })
})
二、 回调地狱

上述代码就是回调地狱,如果处理函数的异步很多,就是异步1里面 有异步2…
就会形成很多嵌套–>回调地狱
不能try catch 捕获错误
不能return,影响美观不易于维护。
缺乏顺序性: 回调地狱导致的调试困难,和大脑的思维方式不符.
嵌套函数存在耦合性,一旦有所改动,就会牵一发而动全身,即(控制反转)
嵌套函数过多的多话,很难处理错误

三 、解决回调地狱使用 ES6中的promise

中文翻译过来承诺,意思是在未来某一个时间点承诺返回数据给你。

介绍promise
  1. Promise有三种状态:pending / resolve / reject。

pending 就是未决的
resolve 就是成功的
reject可以理解为拒绝

  1. 同时Promise有三种常用的方法:

then 表示异步成功执行后的数据状态为resolve;
catch 表示为异步失败后执行的数据状态reject;
all 表示把多个没有关系的Promise封装成一个Promise对象使用then返回一个数组数据。

  1. 使用Promise

a. 构造函数
b. return 一个对象
c. 对象有个 .then()
d. .then(()=>{})有传一个回调函数
e. promise((resolve))
f. resolve() 一调用就会触发外部的 .then的回调

//技巧: 一个异步就是一个Promise对象,调用resolve传递结果
var prom = new Promise((resolve,reject)=>{
     // e.promise((resolve))
     setTimeout(() => {
       var sum = 100
       resolve(sum)
     }, 0);
     })
     // c.对象有个 .then()
     // d. .then(()=>{})有传一个回调函数
     prom.then((res)=>{
       // f. resolve() 一调用就会触发外部的 .then的回调
       console.log('.then 执行了--- ',res); 
     })

Promise构造函数有两个参数的函数:
resolve 用于返回异步执行成功的函数
reject 用于返回异步 执行失败的函数
配合 then与 catch 一起使用

prom('XXX1')
  .then(res => {
      // 操作逻辑
      return ajax('XXX2')
  }).then(res => {
      // 操作逻辑
      return ajax('XXX3')
  }).then(res => {
      // 操作逻辑
  })
  1. 优点:

Promise就是为了解决callback的问题而产生的。
Promise 实现了链式调用,也就是说每次 then 后返回的都是一个全新 Promise
如果我们在 then 中 return ,return 的结果会被 Promise.resolve() 包装
将回调函数的执行和结果比较清晰的分开了,解决了回调地狱的问题

  1. 缺点:

无法取消 Promise ,错误需要通过回调函数来捕获
一堆then,其实并没有将嵌套回调的问题根本上的解决,只是换了一种写法而已。

四、 ES6 co/yield方案 Generator(过渡) —>下一代产品就是 async + await

yield:Generator 函数是协程在 ES6 的实现,而yield是 Generator关键字, 异步操作需要暂停的地方,都用yield语句注明。
co:co 模块是著名程序员 TJ Holowaychuk 于 2013 年 6 月发布的一个小工具,用于 Generator 函数的自动执行。
Generator函数:是协程在 ES6 的实现,最大特点就是可以交出函数的执行权(即主动交出执行权,暂停执行)。
整个 Generator 函数就是一个封装的异步任务,或者说是异步任务的容器。异步操作需要暂停的地方,都用yield语句注明。

  1. Generator 函数的执行方法如下
function *fetch() {
    yield ajax('XXX1', () => {})
    yield ajax('XXX2', () => {})
    yield ajax('XXX3', () => {})
}
let it = fetch()
let result1 = it.next()
let result2 = it.next()
let result3 = it.next()

Generator 函数不同于普通函数的另一个地方,即执行它不会返回结果,返回的是指针对象。
调用指针it的next方法,会移动内部指针(即执行异步任务的第一段),指向第一个遇到的yield语句,发送请求并暂停,再次调用next会发送请求并暂停住

  1. Generator 函数前面要加星号(*)

根据语法规范,yield* 的作用是代理 yield 表达式,将需要函数本身产生(yield)的值委托出去。
yield* 后面跟一个生成器函数、或其他可迭代的对象(如一个数组、字符串、arguments对象)。
yield* 表达式的返回值,就是其后面可迭代对象迭代完毕时的返回值。

  1. 使用
var sayHello = function(name, ms){
    return new Promise(function(resolve, reject){
        setTimeout(function(){
            console.log("hello");
            console.log(name);
            // if (error) return reject(error);
            resolve("Hello World");
        },ms);
    });
}
var gen = function *(){
    yield sayhello("xiaomi", 2000);
    console.log('frist');
    yield sayhello("huawei", 1000);
    console.log('second');
    yield sayhello("apple", 500);
    console.log('end');
}
console.log("mobile phone");
co(gen());
console.log("mobile end");

关于 co/yield详细原理可参考这里:

五、ES7 async/await
1. 说明

async/await是es7的新标准,并且在node7.0中已经得到支持。
它就是 Generator 函数的语法糖,async函数就是将 Generator 函数的星号(*)替换成async,将yield替换成await,仅此而已。

2. 定义
async function fn(sum) {
       setTimeout(async() => {
         var sum1 = 200
         await sum(sum1)
        }, 0);      
     }
3. 介绍

async 关键字用于修饰function;
async函数的特征在于调用return返回的并不是一个普通的值,而是一个promise对象,如果正常return了,则返回promise.resolve(返回值),如果throw一个异常了,则返回promise.reject(异常)
await关键字只能在async函数中使用,不能在任意地方使用await;
await关键字后跟一个promise对象(就是想要执行的异步操作),函数执行到await后会退出该函数,直到事件轮询检查到promise有了resolve和reject状态,才重新执行这个函数后面的内容。

4. 使用
function prom(res,ms){
	return new Promise((resolve,reject)=>{
     setTimeout(() => {
     	console.log("执行了--2遍"); // 第二个执行,调一次函数执行一次
     	console.log(res);
       if(res === 'libai') reject(new Error("后面的不执行"));
       else resolve(res);
     }, ms);
   })
}
async function fn(){
	try {
		console.log("没有阻塞我先执行--1");// 第一个执行
		await prom('zhang--3',3000)
	    console.log("开始--4");
		await prom('libai',2000);// 符合条件后,后面都不执行
		console.log("结束");
		await prom('测试',1000);
	}
	catch (err){
	console.log("出错了" + err.message);
	}
};
fn();

执行结果
在这里插入图片描述

5. 错误的处理

await 命令后的promise对象如果变为reject状态,则reject的参数会被catch方法的回调函数接收到,而后续的await就不会执行了。
我们应该将await语句放在try catch代码中,如果有多个await命令,可以统一放在 try catch结构中,通过这种方式对发生的异步错误进行处理。

关于async可参考这里

6.优缺点

:async、await 是异步的终极解决方案
代码清晰,不用像 Promise 写一大堆 then 链,处理了回调地狱的问题
:await 将异步代码改造成同步代码,如果多个异步操作没有依赖性而使用 await 会导致性能上的降低。

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Friday--星期五

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值