回调函数/异步函数/promise/async,await

相关文章

回调函数

定义

  • 一个函数的执行总是依赖另一个函数的执行结果,函数层层嵌套

举例

  • 回调函数与同步、异步没有关系,回调函数只是一种实现方式,既可以有同步回调,也可以有异步回调,也可以有延迟函数回调
// click事件的回调函数
$('div').click(()=>{
    alert(1)
})

// ajax的回调函数
$.get('/del.html',function(data){
    $('.new').html(data);
})

异步函数

1. 什么是异步函数?

官方

  • 无需等待被调用函数的返回值就继续向下执行的方法

我理解的

  • 异步函数就是将一个函数分成AB两段来做,为避免造成网络堵塞,就把该任务,放在执行栈里面,先执行完同层级的同步函数,再去执行栈拿异步任务出来执行。

2. 异步函数有哪些?

  • 定时器,事件和 ajax

3. js中事件的执行顺序问题

任务分类

  • 同步任务
  • 异步任务

精细划分 =>

  • 宏任务:IO/setTimeout/serInterval
  • 微任务:promise.then(catch/finally)
  • (注:promise是声明立即执行)

执行顺序

  • 依次从上向下执行
    • 遇到同步语句 >> 立即执行
    • 遇到宏任务 >> 放在宏任务队列
    • 遇到微任务 >> 放在微任务队列
  • 先返回执行同级的微任务
    • 遇到同步语句 >> 立即执行
    • 遇到宏任务 >> 放在宏任务队列
    • 遇到微任务 >> 放在微任务队列
  • (同级的微任务队列中没有微任务)再返回执行宏任务
    • 拿出一个宏任务,按照上面的步骤执行即可
      !宏任务的队列是全局的
      !同级的微任务队列没有微任务时再去查找宏任务队列

举例

/*
* 下面的代码b并不会等事件a完全执行完毕在执行
* 在延迟函数被触发的过程中就执行了函数b
* 当js引擎的event 队列空闲时才会去执行队列里等待的setTimeout的回调函数

调用 setTimeout 函数会在一个时间段过去后在队列中添加一个消息。这个时间段作为函数的第二个参数被传入。如果队列中没有其它消息,消息会被马上处理。但是,如果有其它消息,setTimeout 消息必须等待其它消息处理完。因此第二个参数仅仅表示最少的时间 而非确切的时间
*/
function a() {
  setTimeout(() => {
    console.log('执行a函数的延迟函数')
  }, 3000)
  console.log('执行a函数')
}
function b() {
  console.log('执行b函数')
}
a();
b();
// 执行a函数
// VM640:8 执行b函数
// undefined
// VM640:3 执行a函数的延迟函数

4. 回调函数是什么?

定义

  • 一个函数的执行总是依赖另一个函数的执行结果

举例

// click事件的回调函数
$('div').click(()=>{
    alert(1)
})

// ajax的回调函数
$.get('/del.html',function(data){
    $('.new').html(data);
})

Promise

promise解决的什么问题?

  • 回调地狱:下一个事件的执行总是依赖于他的前一个事件的执行结果,嵌套层数过多,难以维护
  • Promise使代码扁平化
  • 回调举例
doSomething(){
    ...
    doSomething2(){
        ...
    }
}

!promise特点

1. promise构造函数立即执行,then是异步执行

const d = new Promise((resolve,reject)=>{
    console.log(1)
resolve()
})
d.then(()=>{
    console.log(3)
})
console.log(2)

2. promise.then()返回一个新的promise,因此可以实行then链式调用

const promise1 = new Promise((resolve,reject)=>{
    console.log(1)
reject()
})
const promise2 = promise1.then(()=>{
    console.log(2)
},()=>{
    console.log(3)
})

console.log(promise1)  
// 打印结果
// Promise {<resolved>: undefined}
// __proto__: Promise
// [[PromiseStatus]]: "rejected"
// [[PromiseValue]]: undefined

console.log(promise2)
// 打印结果
// Promise {<resolved>: undefined}
// __proto__: Promise
// [[PromiseStatus]]: "resolved"
// [[PromiseValue]]: undefined

3. promise的状态只有一次改变机会,一旦改变就不会再变

const promise = new Promise((resolve,reject)=>{
    resolve('success1')
    reject('error')
    resolve('success2')
})
promise.then((res)=>{
    console.log('成功',res)
},(err)=>{
    console.log('失败',err)
})
// '成功' success1

4. Promise只能执行一次,但是then/catch都可以多次调用,且每次调用都能立即拿到promise内部返回值

const promise1 = new Promise((resolve,reject)=>{
    resolve('success')
})
const promise2 = new Promise((resolve,reject)=>{
    reject('error')
})
promise1.then((res)=>{
    console.log(res)
} // success
promise1.then((res)=>{
    console.log(res)
} // success
promise1.catch((res)=>{
    console.log(res)
} // error

5. .then .catch种return一个 Error对象,并不会抛出错误,所以并不会被后续的.catch捕获

  • 因为返回任意一个非 promise 的值都会被包裹成 promise 对象
const promise1 = new Promise((resolve,reject)=>{
    resolve('success')
})
Promise.resolve()
  .then(() => {
    return new Error('error!!!')
  })
  .then((res) => {
    console.log('then: ', res)
  })
  .catch((err) => {
    console.log('catch: ', err)
  })
  
// 打印结果
// then:  Error: error!!!

引出async/await的作用

  • generator函数的语法糖,是模拟generator的*和yield
  • 解决promise层层then ,使得异步代码写的看起来更像同步事件
  • 使用举例: 等待读取文件完成后,再执行console.log打印结果
// 读取文件
const readFile = (originUrl) => {
  return new Promise((resolve, reject) => {
    fs.readFile(originUrl, 'utf-8', (err, data) => {
      if (err) {
        reject(err);
      } else {
        resolve(data)
      }
    });
  })
}

const generateCodeFun = async () => {
    const originCode = await readFile('./test.js');
    console.log(originCode)
}

async特点

  • 使用了async的函数为异步函数
  • async的函数内部自动返回一个状态为resolve的Promise对象:
    • 若内部手动return,则返回一个状态为resolve,参数为return值的Promise;
    • 若无return,返回的是一个状态为resolve,参数为return值的Promise;
  • async内部所有await都执行完了才返回成功还是失败
// 无return
const a = async ()=>{
    console.log(1)
}
a()
// 返回值:Promise {<resolved>: undefined}
const b = async ()=>{
    console.log(1)
    return 1
}
b()
// 返回值:Promise {<resolved>: 1}
  • async返回reject的情况
    • 内部使用未声明的变量或者函数
    • 函数方法使用错误(如,对object使用push)
    • 内部手动抛出一个错误throw new Error或者
    • return Promise.reject('执行失败')
/* 1.使用未声明的变量 */
const a = async ()=>{
    console.log(b);
}
a()
// 返回值: Promise {<rejected>: ReferenceError: b is not defined

/* 2.方法使用错误 */
const b = async ()=>{
    const c = {name:1}
    c.push(1)
}
b()
// 返回值:Promise {<rejected>: TypeError: c.push is not a function

/* 3.手动抛出一个错误 */
const c = async ()=>{
    throw new Error;
}
c()
// 返回值:Promise {<rejected>: Error


/* 4.手动return Promise.reject() */
const e = async ()=>{
    return Promise.reject(1);
}
e()
// 返回值:Promise {<rejected>: 1}

async函数内部一定要return

  • 若手动调用reject,一定要return,不然就认为是resolve,且参数为undefined
// 正确的reject方法。必须将reject状态return出去。
async function PromiseError() {    
   return Promise.reject('有错误');
}
PromiseError()
    .then(success => console.log('成功', success))          
    .catch(error => console.log('失败', error));
    // 打印结果:失败 有错误
    
/*
* 错误的reject方法。必须将reject状态return出去
*/
async function PromiseError() {    
   Promise.reject('有错误');
}
PromiseError()
    .then(success => console.log('成功', success))          
    .catch(error => console.log('失败', error));
    // 打印结果:成功 undefined

await 特点

  • await会暂停当前async function的执行,等待其指定的Promsie处理完成
    • 若promise 成功执行(fulfilled),其回调的resolve()函数的参数作为await表达式的值,继续向下执行async function;
    • 若Promise 执行异常(rejected),await 表达式会抛出 Promise 的异常原因;
    • 若await 等待的表达式的值不是一个Promise,则返回值本身
  • 举例:执行过程:
    • await会等待timeout里面的Promise执行,也就是3秒后将setTimeout内容加入队列
    • 3秒后打印出了1
    • 接着执行console.log(2)
timeout() {
    return new Promise((resolve) => {
      setTimeout(() => {
        console.log(1)
        resolve()
      }, 3000);
    });
}

test = async () => {
    await this.timeout();
    console.log(2)
}
this.test()
  // 3秒后打印
  // 1
  // 2

async函数内部的map内使用await报错await is a reserved word解决方法

  • 解决方法
const generateCodeFun = async (flagArr) => {
+  flagArr.map(async (item) => {
    ...
    return item
  })
}

async/await滥用问题

  • js是单线程执行的,也就是一次只能执行一个,所有的异步都是使用同步模拟出来的,这么做的目的就是为了避免由于网络原因造成堵塞,如果一张图片没有请求到,下面不需要网络的函数们也不能加载执行只能干等着,性能非常不好
  • 而await的提出又是为了解决同步代码需要他之前的异步代码的结果提出的
  • 所以只在需要之前的异步函数的情况下使用await,不然岂不是辜负了设计者的良苦用心

async/await 应用

并行处理多个异步结果:

async function asyncFunc() {
  const [result1, result2] = await Promise.all([
    otherAsyncFunc1(),
    otherAsyncFunc2()
  ]);
  console.log(result1, result2);
}
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 216,496评论 6 501
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,407评论 3 392
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 162,632评论 0 353
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,180评论 1 292
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,198评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,165评论 1 299
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,052评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,910评论 0 274
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,324评论 1 310
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,542评论 2 332
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,711评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,424评论 5 343
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,017评论 3 326
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,668评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,823评论 1 269
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,722评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,611评论 2 353

推荐阅读更多精彩内容