Heim > Schlagzeilen > Hauptteil

Verstehen Sie Promise/Async Wait in einem Artikel und halten Sie sich mit über 70 % der Front-End-Leute in Verbindung

青灯夜游
Freigeben: 2022-09-28 10:39:46
nach vorne
2701 Leute haben es durchsucht

Heute werde ich Versprechen mit Ihnen teilen. Der Autor wird mit dem Dilemma des frühen asynchronen Codes beginnen, welche Probleme die Entstehung von Versprechen löst, die ultimative Lösung für die asynchrone Rückrufhölle und die Implementierung der Kernsyntax von Async /await ist nur eine Variante von generator+promise .

1. Das Dilemma des frühen asynchronen Codes

  • Wie wir alle wissen, ist js Single-Threaded und zeitaufwändige Vorgänge werden vom Browser abgewickelt Warteschlange für die Ausführung, und der Autor hat es auch geteilt. Wenn Sie es verstehen, können Sie promise besser verstehen. promise
  • 我以一个需求为切入点,我模拟网络请求(异步操作)
    • 如果网络请求成功了,你告知我成功了
    • 如果网络请求失败了,你告知我失败了

1.1 大聪明做法

function requestData(url) {
  setTimeout(() => {
    if (url === 'iceweb.io') {
      return '请求成功'
    }
    return '请求失败'
  }, 3000)
}

const result = requestData('iceweb.io')

console.log(result) //undefined
Nach dem Login kopieren
  • 首先你要理解js代码的执行顺序,而不是是想当然的,代码其实并不是按照你书写的顺序执行的。
  • 那么为什么是 undefined呢
    • 首先当我执行requestData函数,开始执行函数。遇到了异步操作不会阻塞后面代码执行的,因为js是单线程的,所以你写的return成功或者失败并没有返回,那我这个函数中,抛开异步操作,里面并没有返回值,所以值为undefined

2.2 早期正确做法

function requestData(url, successCB, failureCB) {
  setTimeout(() => {
    if (url === 'iceweb.io') {
      successCB('我成功了,把获取到的数据传出去', [{name:'ice', age:22}])
    } else {
      failureCB('url错误,请求失败')
    }
  }, 3000)
}

//3s后 回调successCB 
//我成功了,把获取到的数据传出去 [ { name: 'ice', age: 22 } ]
requestData('iceweb.io', (res, data) => console.log(res, data), rej => console.log(rej))

//3s后回调failureCB
//url错误,请求失败
requestData('icexxx.io', res => console.log(res) ,rej => console.log(rej))
Nach dem Login kopieren
  • 早期解决方案都是传入两个回调,一个失败的,一个成功的。那很多开发者会问这不是挺好的吗?挺简单的,js中函数是一等公民,可以传来传去,但是这样太灵活了,没有规范。
  • 如果使用的是框架,还要阅读一下框架源码,正确失败的传实参的顺序,如果传参顺序错误这样是非常危险的。

2. Promise

  • Promise(承诺),给予调用者一个承诺,过一会返回数据给你,就可以创建一个promise对象
  • 当我们new一个promise,此时我们需要传递一个回调函数,这个函数为立即执行的,称之为(executor)
  • 这个回调函数,我们需要传入两个参数回调函数,reslove,reject(函数可以进行传参)
    • 当执行了reslove函数,会回调promise对象的.then函数
    • 当执行了reject函数,会回调promise对象的.catche函数

2.1 Executor立即执行

new Promise((resolve, reject) => {
  console.log(`executor 立即执行`)
})
Nach dem Login kopieren
  • 传入的executor是立即执行的

2.2 requestData 重构

function requestData(url) {
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      if (url === 'iceweb.io') {
        //只能传递一个参数
        resolve('我成功了,把获取到的数据传出去')
      } else {
        reject('url错误,请求失败')
      }
    }, 3000)    
  })
}

//1. 请求成功
requestData('iceweb.io').then(res => {
  //我成功了,把获取到的数据传出去
  console.log(res)
})

//2. 请求失败

//2.2 第一种写法
//url错误,请求失败
requestData('iceweb.org').then(res => {},rej => console.log(rej))

//2.2 第二种写法
//url错误,请求失败
requestData('iceweb.org').catch(e => console.log(e))
Nach dem Login kopieren
  • 在函数中,new这个类的时候,传入的回调函数称之为executor(会被Promise类中自动执行)
  • 在正确的时候调用resolve函数,失败的时候调用reject函数,把需要的参数传递出去。
  • 异常处理
    • 其中在.then方法中可以传入两个回调,您也可以查看Promise/A+规范
      • 第一个则是fulfilled的回调
      • 第二个则是rejected的回调
  • 那这样有什么好处呢? 看起来比早期处理的方案还要繁琐呢?
    • 统一规范,可以增强阅读性和扩展性

    • 小幅度减少回调地狱

2.3 promise的状态

  • 首先先给大家举个栗子,把代码抽象为现实的栗子
    • 你答应你女朋友,下周末带她去吃好吃的 (还未到下周末,此时状态为待定状态)
    • 时间飞快,今天就是周末了,你和你女友一起吃了烤肉、甜点、奶茶...(已兑现状态
    • 时间飞快,今天就是周末了,正打算出门。不巧产品经理,因为线上出现的紧急问题,需要回公司解决一下,你(为了生活)只能委婉的拒绝一下女友,并且说明一下缘由(已拒绝状态)
  • 使用promise的时候,给它一个承诺,我们可以将他划分为三个阶段
    • pending(待定),执行了executor,状态还在等待中,没有被兑现,也没有被拒绝
    • fulfilled(已兑现),执行了resolve函数则代表了已兑现状态
    • rejected(已拒绝),执行了reject
    • Ich verwende eine Anforderung als Einstiegspunkt, ich simuliere eine Netzwerkanforderung (asynchroner Vorgang)
  • Wenn die Netzwerkanforderung erfolgreich ist, sagen Sie mir, dass sie erfolgreich ist
  • Wenn die Netzwerkanforderung fehlschlägt, sagen Sie mir, dass sie fehlgeschlagen ist

🎜

🎜1.1 Ein intelligenter Ansatz🎜

const promise = new Promise((resolve, reject) => {
  setTimeout(() => {
    reject('失败')
    resolve('成功')
  }, 3000);
})

promise.then(res => console.log(res)).catch(err => console.log(err))

//失败
Nach dem Login kopieren
Nach dem Login kopieren
🎜🎜Zuallererst müssen Sie stattdessen die Ausführungsreihenfolge des js-Codes verstehen es als selbstverständlich zu betrachten, ist der Code tatsächlich nicht. Befolgen Sie die Reihenfolge, die Sie geschrieben haben. 🎜🎜Warum ist es also undefiniert? 🎜🎜Zuallererst beginne ich mit der Ausführung der Funktion, wenn ich die Funktion requestData ausführe. Wenn ein asynchroner Vorgang auftritt, wird die Ausführung des folgenden Codes nicht blockiert, da js ein Single-Thread-Vorgang ist, sodass der von Ihnen geschriebene return nicht zurückgegeben wird, wenn er erfolgreich ist oder fehlschlägt. Dann gilt in dieser Funktion: Ich habe die asynchrone Operation beiseite gelegt. Es gibt keinen Rückgabewert, daher ist der Wert undefiniert. 🎜🎜🎜🎜

🎜2.2 Frühzeitiger korrekter Ansatz🎜

const promise = new Promise((resolve, reject) => {
  resolve({name: 'ice', age: 22})
})

promise.then(res => console.log(res))

// {name: 'ice', age: 22}
Nach dem Login kopieren
Nach dem Login kopieren
🎜🎜Die erste Lösung bestand darin, zwei Rückrufe einzugeben, einen für Misserfolg und einen für Erfolg. Dann werden sich viele Entwickler fragen: Ist das nicht großartig? Es ist ganz einfach. Funktionen in js sind erstklassige Bürger und können weitergegeben werden, aber das ist zu flexibel und hat keine Spezifikationen. 🎜🎜Wenn Sie ein Framework verwenden, sollten Sie auch den Quellcode des Frameworks lesen und die tatsächlichen Parameter in der fehlgeschlagenen Reihenfolge korrekt übergeben. Es ist sehr gefährlich, wenn die Parameter in der falschen Reihenfolge übergeben werden. 🎜🎜

🎜2. Versprechen🎜🎜🎜🎜Versprechen (Versprechen), geben Sie dem Anrufer ein Versprechen und geben Sie die Daten nach einer Weile an Sie zurück ein Versprechensobjekt🎜🎜Wenn wir ein Versprechen neu, müssen wir eine Rückruffunktion übergeben. Diese Funktion wird sofort ausgeführt und heißt (Executor)🎜🎜 Für diese Rückruffunktion , müssen wir zwei Parameter an die Rückruffunktion übergeben, reslove, reject (die Funktion kann Parameter übergeben)🎜🎜Wenn resloveausgeführt wird > Die Funktion ruft die .then-Funktion des Promise-Objekts zurück🎜🎜Wenn die Funktion reject ausgeführt wird, wird die .catche-Funktion des Promise-Objekts zurückgerufen🎜🎜🎜🎜

🎜2.1 Executor wird sofort ausgeführt🎜

const promise = new Promise((resolve, reject) => {
  resolve(new Promise((resolve, reject) => {
    setTimeout(() => {
      resolve('ice')
    }, 3000);
  }))
})

promise.then(res => console.log(res))

//3s后 ice
Nach dem Login kopieren
Nach dem Login kopieren
🎜🎜Der eingehende executor wird sofort ausgeführt🎜🎜

🎜2.2 requestData Rekonstruktion🎜 h3>
const promise = new Promise((resolve, reject) => {
  resolve({
    then(res, rej) {
      res('hi ice')
    }
  })
})

promise.then(res => console.log(res))

// hi ice
Nach dem Login kopieren
Nach dem Login kopieren
🎜🎜Wenn in der Funktion diese Klasse neu ist, heißt die übergebene Rückruffunktion executor (sie wird automatisch in der Promise-Klasse ausgeführt)🎜🎜Rufen Sie auf zum richtigen Zeitpunkt Die Funktion „resolve ruft die Funktion „reject“ auf, wenn sie fehlschlägt, und übergibt die erforderlichen Parameter. 🎜🎜Ausnahmebehandlung🎜🎜Zwei Rückrufe können in der .then-Methode übergeben werden, Sie können auch Promise/A+ Spezifikation🎜🎜Der erste ist der Rückruf von erfüllt🎜🎜Der zweite ist der Rückruf von abgelehnt🎜 🎜🎜🎜🎜 🎜Was sind dann die Vorteile davon? Erscheint es umständlicher als die frühen Lösungen?
    🎜🎜Einheitliche Spezifikationen können die Lesbarkeit und Skalierbarkeit verbessern🎜🎜🎜🎜Die Rückrufhölle etwas reduzieren🎜🎜 🎜🎜🎜🎜2.3 Versprechensstatus🎜

🎜🎜Lassen Sie mich zunächst ein Beispiel geben und den Code in ein realistisches Beispiel abstrahieren.🎜🎜Sie versprechen Ihrer Freundin, sie mitzunehmen, um lecker zu essen Essen am nächsten Wochenende (es ist noch nicht nächstes Wochenende, der Status ist 🎜Ausstehender Status🎜)🎜🎜Die Zeit vergeht wie im Flug, heute ist das Wochenende, du und deine Freundin haben gegrillt, Desserts, Milchtee ... (🎜Cashed-Status🎜)🎜🎜 Die Zeit vergeht wie im Flug, heute ist Wochenende und ich habe vor, auszugehen. Leider muss der Produktmanager zum Unternehmen zurückkehren, um ein dringendes Problem zu lösen, das online aufgetreten ist. Sie können Ihre Freundin (um des Lebens willen) nur höflich ablehnen und den Grund erklären (🎜Abgelehnter Status🎜)🎜🎜🎜🎜Nutzung promise, geben Sie ihm ein Versprechen, wir können es in drei Phasen unterteilen🎜🎜pending (ausstehend), der Executor wird ausgeführt, der Status wartet noch, wurde nicht eingehalten und wurde nicht abgelehnt🎜 🎜 erfüllt (erfüllt), die Ausführung der Funktion resolve stellt den Status „erfüllt“ dar 🎜🎜abgelehnt (abgelehnt), die Ausführung der Funktion reject stellt den Status „abgelehnt“ dar 🎜🎜 🎜🎜Zuallererst , solange sich der Status vom Status „Ausstehend“ in einen anderen Status ändert, kann der Status nicht mehr geändert werden🎜🎜🎜Denken Sie an den folgenden Code:🎜
const promise = new Promise((resolve, reject) => {
  setTimeout(() => {
    reject('失败')
    resolve('成功')
  }, 3000);
})

promise.then(res => console.log(res)).catch(err => console.log(err))

//失败
Nach dem Login kopieren
Nach dem Login kopieren
  • 当我调用reject之后,在调用resolve是无效的,因为状态已经发生改变,并且是不可逆的。

2.4 resolve不同值的区别

  • 如果resolve传入一个普通的值或者对象,只能传递接受一个参数,那么这个值会作为then回调的参数
const promise = new Promise((resolve, reject) => {
  resolve({name: 'ice', age: 22})
})

promise.then(res => console.log(res))

// {name: 'ice', age: 22}
Nach dem Login kopieren
Nach dem Login kopieren
  • 如果resolve中传入的是另外一个Promise,那么这个新Promise会决定原Promise的状态
const promise = new Promise((resolve, reject) => {
  resolve(new Promise((resolve, reject) => {
    setTimeout(() => {
      resolve('ice')
    }, 3000);
  }))
})

promise.then(res => console.log(res))

//3s后 ice
Nach dem Login kopieren
Nach dem Login kopieren
  • 如果resolve中传入的是一个对象,并且这个对象有实现then方法,那么会执行该then方法,then方法会传入resolvereject函数。此时的promise状态取决于你调用了resolve,还是reject函数。这种模式也称之为: thenable
const promise = new Promise((resolve, reject) => {
  resolve({
    then(res, rej) {
      res('hi ice')
    }
  })
})

promise.then(res => console.log(res))

// hi ice
Nach dem Login kopieren
Nach dem Login kopieren

2.5 Promise的实例方法

  • 实例方法,存放在Promise.prototype上的方法,也就是Promise的显示原型上,当我new Promise的时候,会把返回的改对象的 promise[[prototype]](隐式原型) === Promise.prototype (显示原型)
  • 即new返回的对象的隐式原型指向了Promise的显示原型

2.5.1 then方法

2.5.1.1 then的参数
  • then方法可以接受参数,一个参数为成功的回调,另一个参数为失败的回调,前面重构requestData中有演练过。
const promise = new Promise((resolve, reject) => {
  resolve('request success')
  // reject('request error')
})

promise.then(res => console.log(res), rej => console.log(rej))

//request success
Nach dem Login kopieren
  • 如果只捕获错误,还可以这样写
    • 因为第二个参数是捕获异常的,第一个可以写个null""占位
const promise = new Promise((resolve, reject) => {
  // resolve('request success')
  reject('request error')
})

promise.then(null, rej => console.log(rej))

//request error
Nach dem Login kopieren
2.5.1.2 then的多次调用
const promise = new Promise((resolve, reject) => {
  resolve('hi ice')
})

promise.then(res => ({name:'ice', age:22}))
       .then(res => console.log(res))
       
//{name:'ice', age:22}
Nach dem Login kopieren
Nach dem Login kopieren
  • 调用多次则会执行多次
2.5.1.3 then的返回值
  • then方法是有返回值的,它的返回值是promise,但是是promise那它的状态如何决定呢?接下来让我们一探究竟。
2.5.1.3.1 返回一个普通值 状态:fulfilled
const promise = new Promise((resolve, reject) => {
  resolve('hi ice')
})

promise.then(res => ({name:'ice', age:22}))
       .then(res => console.log(res))
       
//{name:'ice', age:22}
Nach dem Login kopieren
Nach dem Login kopieren
  • 返回一个普通值,则相当于主动调用Promise.resolve,并且把返回值作为实参传递到then方法中。
  • 如果没有返回值,则相当于返回undefined
2.5.1.3.2 明确返回一个promise 状态:fulfilled
const promise = new Promise((resolve, reject) => {
  resolve('hi ice')
})

promise.then(res => {
  return new Promise((resolve, reject) => {
    resolve('then 的返回值')
  })
}).then(res => console.log(res))

//then 的返回值
Nach dem Login kopieren
  • 主动返回一个promise对象,状态和你调用resolve,还是reject有关
2.5.1.3.3 返回一个thenable对象 状态:fulfilled
const promise = new Promise((resolve, reject) => {
  resolve('hi ice')
})

promise.then(res => {
  return {
    then(resolve, reject) {
      resolve('hi webice')
    }
  }
}).then(res => console.log(res))

//hi webice
Nach dem Login kopieren
  • 返回了一个thenable对象,其状态取决于你是调用了resolve,还是reject

2.5.2 catch方法

2.5.2.1 catch的多次调用
const promise = new Promise((resolve, reject) => {
  reject('ice error')
})

promise.catch(err => console.log(err))
promise.catch(err => console.log(err))
promise.catch(err => console.log(err))

//ice error
//ice error
//ice error
Nach dem Login kopieren
2.5.2.2 catch的返回值
  • catch方法是有返回值的,它的返回值是promise,但是是promise那它的状态如何决定呢?接下来让我们一探究竟。
  • 如果返回值明确一个promise或者thenble对象,取决于你调用了resolve还是reject
2.5.2.2.1 返回一个普通对象
const promise = new Promise((resolve, reject) => {
  reject('ice error')
})

promise.catch(err => ({name:'ice', age: 22})).then(res => console.log(res))

//{name:'ice', age: 22}
Nach dem Login kopieren
2.5.2.2.2 明确返回一个promise
const promise = new Promise((resolve, reject) => {
  reject('ice error')
})

promise.catch(err => {
  return new Promise((resolve, reject) => {
    reject('ice error promise')
  })
}).catch(res => console.log(res))

//ice error promise
Nach dem Login kopieren
  • 此时new Promise() 调用了reject函数,则会被catch捕获到
2.5.2.2.3 返回thenble对象
const promise = new Promise((resolve, reject) => {
  reject('ice error')
})

promise.catch(err => {
  return {
    then(resolve, reject) {
      reject('ice error then')
    }
  }
}).catch(res => console.log(res))

//ice error then
Nach dem Login kopieren

2.5.3 finally方法

  • ES9(2018)新实例方法
  • finally(最后),无论promise状态是fulfilled还是rejected都会执行一次finally方法
const promise = new Promise((resolve, reject) => {
  resolve('hi ice')
})

promise.then(res => console.log(res)).finally(() => console.log('finally execute'))

//finally execute
Nach dem Login kopieren

2.6 Promise中的类方法/静态方法

2.6.1 Promise.reslove

Promise.resolve('ice')
//等价于
new Promise((resolve, reject) => resolve('ice'))
Nach dem Login kopieren
  • 有的时候,你已经预知了状态的结果为fulfilled,则可以用这种简写方式

2.6.2 Promise.reject

Promise.reject('ice error')
//等价于
new Promise((resolve, reject) => reject('ice error'))
Nach dem Login kopieren
  • 有的时候,你已经预知了状态的结果为rejected,则可以用这种简写方式

2.6.3 Promise.all

fulfilled 状态

const promise1 = new Promise((resolve, reject) => {
  setTimeout(() => {
    resolve('hi ice')
  }, 1000);
})

const promise2 = new Promise((resolve, reject) => {
  setTimeout(() => {
    resolve('hi panda')
  }, 2000);
})

const promise3 = new Promise((resolve, reject) => {
  setTimeout(() => {
    resolve('hi grizzly')
  }, 3000);
})


Promise.all([promise1, promise2, promise3]).then(res => console.log(res))

//[ 'hi ice', 'hi panda', 'hi grizzly' ]
Nach dem Login kopieren
  • all方法的参数传入为一个可迭代对象,返回一个promise,只有三个都为resolve状态的时候才会调用.then方法。
  • 只要有一个promise的状态为rejected,则会回调.catch方法

rejected状态

const promise1 = new Promise((resolve, reject) => {
  setTimeout(() => {
    resolve('hi ice')
  }, 1000);
})

const promise2 = new Promise((resolve, reject) => {
  setTimeout(() => {
    reject('hi panda')
  }, 2000);
})

const promise3 = new Promise((resolve, reject) => {
  setTimeout(() => {
    resolve('hi grizzly')
  }, 3000);
})

Promise.all([promise1, promise2, promise3]).then(res => console.log(res)).catch(err => console.log(err))

//hi panda
Nach dem Login kopieren
  • 当遇到rejectd的时候,后续的promise结果我们是获取不到,并且会把reject的实参,传递给catch的err形参中

2.6.4 Promise.allSettled

  • 上面的Promise.all有一个缺陷,就是当遇到一个rejected的状态,那么对于后面是resolve或者reject的结果我们是拿不到的
  • ES11 新增语法Promise.allSettled,无论状态是fulfilled/rejected都会把参数返回给我们

所有promise都有结果

const promise1 = new Promise((resolve, reject) => {
  setTimeout(() => {
    reject('hi ice')
  }, 1000);
})

const promise2 = new Promise((resolve, reject) => {
  setTimeout(() => {
    resolve('hi panda')
  }, 2000);
})

const promise3 = new Promise((resolve, reject) => {
  setTimeout(() => {
    reject('hi grizzly')
  }, 3000);
})

Promise.allSettled([promise1, promise2, promise3]).then(res => console.log(res))

/* [
  { status: 'rejected', reason: 'hi ice' },
  { status: 'fulfilled', value: 'hi panda' },
  { status: 'rejected', reason: 'hi grizzly' }
] */
Nach dem Login kopieren
  • 该方法会在所有的Promise都有结果,无论是fulfilled,还是rejected,才会有最终的结果

其中一个promise没有结果

const promise1 = new Promise((resolve, reject) => {
  setTimeout(() => {
    reject('hi ice')
  }, 1000);
})

const promise2 = new Promise((resolve, reject) => {
  setTimeout(() => {
    resolve('hi panda')
  }, 2000);
})

const promise3 = new Promise((resolve, reject) => {})


Promise.allSettled([promise1, promise2, promise3]).then(res => console.log(res))
// 什么都不打印
Nach dem Login kopieren
  • 其中一个promise没有结果,则什么都结果都拿不到

2.6.5 Promise.race

  • race(竞争竞赛)
  • 优先获取第一个返回的结果,无论结果是fulfilled还是rejectd
const promise1 = new Promise((resolve, reject) => {
  setTimeout(() => {
    reject('hi error')
  }, 1000);
})

const promise2 = new Promise((resolve, reject) => {
  setTimeout(() => {
    resolve('hi panda')
  }, 2000);
})


Promise.race([promise1, promise2])
       .then(res => console.log(res))
       .catch(e => console.log(e))
       
//hi error
Nach dem Login kopieren

2.6.6 Promise.any

  • 与race类似,只获取第一个状态为fulfilled,如果全部为rejected则报错AggregateError
const promise1 = new Promise((resolve, reject) => {
  setTimeout(() => {
    reject('hi error')
  }, 1000);
})

const promise2 = new Promise((resolve, reject) => {
  setTimeout(() => {
    resolve('hi panda')
  }, 2000);
})


Promise.any([promise1, promise2])
       .then(res => console.log(res))
       .catch(e => console.log(e))
       
//hi panda
Nach dem Login kopieren

3. Promise的回调地狱 (进阶)

  • 我还是以一个需求作为切入点,把知识点嚼碎了,一点一点喂进你们嘴里。
    • 当我发送网络请求的时候,需要拿到这次网络请求的数据,再发送网络请求,就这样重复三次,才能拿到我最终的结果。

3.1 卧龙解法

function requestData(url) {
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      if (url.includes('iceweb')) {
        resolve(url)
      } else {
        reject('请求错误')
      }
    }, 1000);
  })
}


requestData('iceweb.io').then(res => {
  requestData(`iceweb.org ${res}`).then(res => {
    requestData(`iceweb.com ${res}`).then(res => {
      console.log(res)
    })
  })
})

//iceweb.com iceweb.org iceweb.io
Nach dem Login kopieren
  • 虽然能够实现,但是多层代码的嵌套,可读性非常差,我们把这种多层次代码嵌套称之为回调地狱

3.2 凤雏解法

function requestData(url) {
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      if (url.includes('iceweb')) {
        resolve(url)
      } else {
        reject('请求错误')
      }
    }, 1000);
  })
}

requestData('iceweb.io').then(res => {
  return requestData(`iceweb.org ${res}`)
}).then(res => {
  return requestData(`iceweb.com ${res}`)
}).then(res => {
  console.log(res)
})

//iceweb.com iceweb.org iceweb.io
Nach dem Login kopieren
  • 利用了then链式调用这一特性,返回了一个新的promise,但是不够优雅,思考一下能不能写成同步的方式呢?

3.3 生成器+Promise解法

function requestData(url) {
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      if (url.includes('iceweb')) {
        resolve(url)
      } else {
        reject('请求错误')
      }
    }, 1000);
  })
}

function* getData(url) {
  const res1 = yield requestData(url)
  const res2 = yield requestData(res1)
  const res3 = yield requestData(res2)

  console.log(res3)
}

const generator = getData('iceweb.io')

generator.next().value.then(res1 => {
  generator.next(`iceweb.org ${res1}`).value.then(res2 => {
    generator.next(`iceweb.com ${res2}`).value.then(res3 => {
      generator.next(res3)
    })
  })
})

//iceweb.com iceweb.org iceweb.io
Nach dem Login kopieren
  • 大家可以发现我们的getData已经变为同步的形式,可以拿到我最终的结果了。那么很多同学会问,generator一直调用.next不是也产生了回调地狱吗?
  • 其实不用关心这个,我们可以发现它这个是有规律的,我们可以封装成一个自动化执行的函数,我们就不用关心内部是如何调用的了。

3.4 自动化执行函数封装

function requestData(url) {
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      if (url.includes('iceweb')) {
        resolve(url)
      } else {
        reject('请求错误')
      }
    }, 1000);
  })
}

function* getData() {
  const res1 = yield requestData('iceweb.io')
  const res2 = yield requestData(`iceweb.org ${res1}`)
  const res3 = yield requestData(`iceweb.com ${res2}`)

  console.log(res3)
}

//自动化执行 async await相当于自动帮我们执行.next
function asyncAutomation(genFn) {
  const generator = genFn()

  const _automation = (result) => {
    let nextData = generator.next(result)
    if(nextData.done) return

    nextData.value.then(res => {
      _automation(res)
    })
  }

  _automation()
}

asyncAutomation(getData)

//iceweb.com iceweb.org iceweb.io
Nach dem Login kopieren
  • 利用promise+生成器的方式变相实现解决回调地狱问题,其实就是async await的一个变种而已
  • 最早为TJ实现,前端大神人物
  • async await核心代码就类似这些,内部主动帮我们调用.next方法

3.5 最终解决回调地狱的办法

function requestData(url) {
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      if (url.includes('iceweb')) {
        resolve(url)
      } else {
        reject('请求错误')
      }
    }, 1000);
  })
}

async function getData() {
  const res1 = await requestData('iceweb.io')
  const res2 = await requestData(`iceweb.org ${res1}`)
  const res3 = await requestData(`iceweb.com ${res2}`)

  console.log(res3)
}

getData()

//iceweb.com iceweb.org iceweb.io
Nach dem Login kopieren
  • 你会惊奇的发现,只要把getData生成器函数函数,改为async函数,yeild的关键字替换为await就可以实现异步代码同步写法了。

4. async/await 剖析

  • async(异步的)
  • async 用于申明一个异步函数

4.1 async内部代码同步执行

  • 异步函数的内部代码执行过程和普通的函数是一致的,默认情况下也是会被同步执行
async function sayHi() {
  console.log('hi ice')
}

sayHi()

//hi ice
Nach dem Login kopieren

4.2 异步函数的返回值

  • 异步函数的返回值和普通返回值有所区别

    • 普通函数主动返回什么就返回什么,不返回为undefined
    • 异步函数的返回值特点
      • 明确有返回一个普通值,相当于Promise.resolve(返回值)
      • 返回一个thenble对象则由,then方法中的resolve,或者reject有关
      • 明确返回一个promise,则由这个promise决定
  • 异步函数中可以使用await关键字,现在在全局也可以进行await,但是不推荐。会阻塞主进程的代码执行

4.3 异步函数的异常处理

  • 如果函数内部中途发生错误,可以通过try catch的方式捕获异常
  • 如果函数内部中途发生错误,也可以通过函数的返回值.catch进行捕获
async function sayHi() {
  console.log(res)
}
sayHi().catch(e => console.log(e))

//或者

async function sayHi() {
  try {
    console.log(res)
  }catch(e) {
    console.log(e)
  }
}

sayHi()

//ReferenceError: res is not defined
Nach dem Login kopieren

4.4 await 关键字

  • 异步函数中可以使用await关键字,普通函数不行
  • await特点
    • 通常await关键字后面都是跟一个Promise
      • 可以是普通值
      • 可以是thenble
      • 可以是Promise主动调用resolve或者reject
    • 这个promise状态变为fulfilled才会执行await后续的代码,所以await后面的代码,相当于包括在.then方法的回调中,如果状态变为rejected,你则需要在函数内部try catch,或者进行链式调用进行.catch操作
function requestData(url) {
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      if (url.includes('iceweb')) {
        resolve(url)
      } else {
        reject('请求错误')
      }
    }, 1000);
  })
}

async function getData() {
  const res = await requestData('iceweb.io')
  console.log(res)
}

getData()

// iceweb.io
Nach dem Login kopieren

5. 结语

  • 如果现在真的看不到未来是怎样,你就不如一直往前走,不知道什么时候天亮,去奔跑就好,跑着跑着天就亮了。

【相关推荐:javascript视频教程编程基础视频

Verwandte Etiketten:
Quelle:juejin.cn
Erklärung dieser Website
Der Inhalt dieses Artikels wird freiwillig von Internetnutzern beigesteuert und das Urheberrecht liegt beim ursprünglichen Autor. Diese Website übernimmt keine entsprechende rechtliche Verantwortung. Wenn Sie Inhalte finden, bei denen der Verdacht eines Plagiats oder einer Rechtsverletzung besteht, wenden Sie sich bitte an admin@php.cn
Beliebte Tutorials
Mehr>
Neueste Downloads
Mehr>
Web-Effekte
Quellcode der Website
Website-Materialien
Frontend-Vorlage