如何解决异步回调地狱


promise、generator、async/await

什么是async?

欢迎留言讨论

async 函数是 Generator 函数的语法糖。使用 关键字 async 来表示,在函数内部使用 await 来表示异步。相较于 Generatorasync 函数的改进在于下面四点:

  • 内置执行器Generator 函数的执行必须依靠执行器,而 async 函数自带执行器,调用方式跟普通函数的调用一样

  • 更好的语义asyncawait 相较于 *yield 更加语义化

  • 更广的适用性co 模块约定,yield 命令后面只能是 Thunk 函数或 Promise对象。而 async 函数的 await 命令后面则可以是 Promise 或者 原始类型的值(Number,string,boolean,但这时等同于同步操作)

  • 返回值是 Promiseasync 函数返回值是 Promise 对象,比 Generator 函数返回的 Iterator 对象方便,可以直接使用 then() 方法进行调用

此处总结参考自:理解async/await

async是ES7新出的特性,表明当前函数是异步函数,不会阻塞线程导致后续代码停止运行。

怎么用

申明之后就可以进行调用了

async function asyncFn() {   return 'hello world'; } asyncFn();复制代码

这样就表示这是异步函数,返回的结果

     

返回的是一个promise对象,状态为resolved,参数是return的值。那再看下面这个函数

async function asyncFn() { return '我后执行' } asyncFn().then(result => { console.log(result); }) console.log('我先执行');复制代码

 

上面的执行结果是先打印出'我先执行',虽然是上面asyncFn()先执行,但是已经被定义异步函数了,不会影响后续函数的执行。

 

现在理解了async基本的使用,那还有什么特性呢?

async定义的函数内部会默认返回一个promise对象,如果函数内部抛出异常或者是返回reject,都会使函数的promise状态为失败reject

 

 

 

async function e() { throw new Error('has Error'); } e().then(success => console.log('成功', success)) .catch(error => console.log('失败', error));复制代码

 

我们看到函数内部抛出了一个异常,返回rejectasync函数接收到之后,判定执行失败进入catch,该返回的错误打印了出来。

 

 

 

async function throwStatus() { return '可以返回所有类型的值' } throwStatus().then(success => console.log('成功', success)) .catch(error => console.log('失败', error));复制代码

 

 

 

 

//打印结果

成功 可以返回所有类型的值
复制代码

 

async函数接收到返回的值,发现不是异常或者reject,则判定成功,这里可以return各种数据类型的值,false,NaN,undefined...总之,都是resolve

但是返回如下结果会使async函数判定失败reject

  1. 内部含有直接使用并且未声明的变量或者函数。
  2. 内部抛出一个错误throw new Error或者返回reject状态return Promise.reject('执行失败')
  3. 函数方法执行出错(🌰:Object使用push())等等...

还有一点,在async里,必须要将结果return回来,不然的话不管是执行reject还是resolved的值都为undefine,建议使用箭头函数。

其余返回结果都是判定resolved成功执行。

 

 

 

//正确reject方法。必须将reject状态return出去。 async function PromiseError() { return Promise.reject('has Promise Error'); } //这是错误的做法,并且判定resolve,返回值为undefined,并且Uncaught报错 async function PromiseError() { Promise.reject('这是错误的做法'); } PromiseError().then(success => console.log('成功', success)) .catch(error => console.log('失败', error));复制代码

 

 

 

我们看到第二行多了个Promise对象打印,不用在意,这个是在Chrome控制台的默认行为,我们平常在控制台进行赋值也是同样的效果。如果最后执行语句或者表达式没有return返回值,默认undefined,做个小实验。

 

 

 

var a = 1;
//undefined
------------------------------------------------------------
console.log(a);
//1
//undefined
------------------------------------------------------------
function a(){ console.log(1) } a(); //1 //undefined ------------------------------------------------------------ function b(){ return console.log(1) } b(); //1 //undefined ------------------------------------------------------------ function c(){ return 1} c(); //1 ------------------------------------------------------------ async function d(){ '这个值接收不到' } d().then(success => console.log('成功',success)); //成功 undefined //Promise { <resolved>: undefined } ----------------------------------------------------------- async function e(){ return '接收到了' } e().then(success => console.log('成功',success)); //成功 接收到了 //Promise { <resolved>: undefined }复制代码

 

最后一行Promise { <resolved> : undefined } 是因为返回的是console.log执行语句,没有返回值。

 

 

 

d().then(success => console.log('成功',success)} 等同于 d().then(function(success){ return console.log('成功',success); }); 复制代码

 

认识完了async,来讲讲await。

await是什么?

await意思是async wait(异步等待)。这个关键字只能在使用async定义的函数里面使用。任何async函数都会默认返回promise,并且这个promise解析的值都将会是这个函数的返回值,而async函数必须等到内部所有的 await 命令的 Promise 对象执行完,才会发生状态改变。

打个比方,await是学生,async是校车,必须等人齐了再开车。

就是说,必须等所有 await 函数执行完毕后,才会告诉 promise我成功了还是失败了,执行 then或者 catch

 

 

 

async function awaitReturn() { return await 1 }; awaitReturn().then(success => console.log('成功', success)) .catch(error => console.log('失败',error))复制代码

 

在这个函数里,有一个await函数,async会等到await 1 这一步执行完了才会返回promise状态,毫无疑问,判定resolved

很多人以为await会一直等待之后的表达式执行完之后才会继续执行后面的代码,实际上await是一个让出线程的标志await后面的函数会先执行一遍,然后就会跳出整个async函数来执行后面js栈的代码。等本轮事件循环执行完了之后又会跳回到async函数中等待await后面表达式的返回值,如果返回值为非promise则继续执行async函数后面的代码,否则将返回的promise放入Promise队列(Promise的Job Queue)

来看个简单点的例子

 

 

 

const timeoutFn = function(timeout){ return new Promise(function(resolve){ return setTimeout(resolve, timeout); }); } async function fn(){ await timeoutFn(1000); await timeoutFn(2000); return '完成'; } fn().then(success => console.log(success));复制代码

 

这里本可以用箭头函数写方便点,但是为了便于阅读本质,还是换成了ES5写法,上面执行函数内所有的await函数才会返回状态,结果是执行完毕3秒后才会弹出'完成'。

正常情况下,await 命令后面跟着的是 Promise ,如果不是的话,也会被转换成一个 立即 resolve 的 Promise。

也可以这么写

 

 

 

function timeout(time){ return new Promise(function(resolve){ return setTimeout(function(){ return resolve(time + 200) },time); }) } function first(time){ console.log('第一次延迟了' + time ); return timeout(time); } function second(time){ console.log('第二次延迟了' + time ); return timeout(time); } function third(time){ console.log('第三次延迟了' + time ); return timeout(time); } function start(){ console.log('START'); const time1 = 500; first(time1).then(time2 => second(time2) ) .then(time3 => third(time3) ) .then(res => { console.log('最后一次延迟' + res ); console.timeEnd('END'); }) }; start();复制代码

这样用then链式回调的方式执行resolve

 

 

 

//打印结果

START
第一次延迟了500
第二次延迟了700
第三次延迟了900
最后一次延迟1100
END
复制代码

 

用async/await呢?

 

 

 

async function start() { console.log('START'); const time1 = 500; const time2 = await first(time1); const time3 = await second(time2); const res = await third(time3); console.log(`最后一次延迟${res}`); console.log('END'); } start();复制代码

达到了相同的效果。但是这样遇到一个问题,如果await执行遇到报错呢

 

 

 

async function start() { console.log('START'); const time1 = 500; const time2 = await first(time1); const time3 = await Promise.reject(time2); const res = await third(time3); console.log(`最后一次延迟${res}`); console.log('END'); } start();复制代码

 

返回reject后,后面的代码都没有执行了,以此迁出一个例子:

 

 

 

let last; async function throwError() { await Promise.reject('error'); last = await '没有执行'; } throwError().then(success => console.log('成功', last)) .catch(error => console.log('失败',last))复制代码

 

 

其实
async函数不难,难在错处理上。

 

上面函数,执行的到await排除一个错误后,就停止往下执行,导致last没有赋值报错。

async里如果有多个await函数的时候,如果其中任一一个抛出异常或者报错了,都会导致函数停止执行,直接 reject;

怎么处理呢,可以用try/catch,遇到函数的时候,可以将错误抛出,并且继续往下执行。

 

 

 

let last; async function throwError() { try{ await Promise.reject('error'); last = await '没有执行'; }catch(error){ console.log('has Error stop'); } } throwError().then(success => console.log('成功', last)) .catch(error => console.log('失败',last))复制代码

这样的话,就可以继续往下执行了。

 

 

来个🌰练习下

 

 

 

 

function testSometing() { console.log("testSomething"); return "return testSomething"; } async function testAsync() { console.log("testAsync"); return Promise.resolve("hello async"); } async function test() { console.log("test start..."); const testFn1 = await testSometing(); console.log(testFn1); const testFn2 = await testAsync(); console.log(testFn2); console.log('test end...'); } test(); var promiseFn = new Promise((resolve)=> { console.log("promise START..."); resolve("promise RESOLVE"); }); promiseFn.then((val)=> console.log(val)); console.log("===END===")复制代码

 


免责声明!

本站转载的文章为个人学习借鉴使用,本站对版权不负任何法律责任。如果侵犯了您的隐私权益,请联系本站邮箱yoyou2525@163.com删除。



 
粤ICP备18138465号  © 2018-2025 CODEPRJ.COM