天天看點

♠ 異步處理方案

♣ 異步處理方案

需求:

  1. 我們需要向伺服器發送網絡請求擷取資料,一共需要發送三次請求;
  2. 第二次的請求url依賴于第一次的結果;
  3. 第三次的請求url依賴于第二次的結果;

依次類推;

多次回調、Promise中then

點選檢視代碼

function requestData(url) {
  // 異步請求的代碼會被放入到executor中
  return new Promise((resolve, reject) => {
    // 模拟網絡請求
    setTimeout(() => {
      // 拿到請求的結果
      resolve(url)
    }, 2000);
  })
}

// 需求: 
// 1> url: url -> res: url
// 2> url: res + "aaa" -> res: urlaaa
// 3> url: res + "bbb" => res: urlaaabbb

// 1.第一種方案: 多次回調
// 回調地獄
// requestData("url").then(res => {
//   requestData(res + "aaa").then(res => {
//     requestData(res + "bbb").then(res => {
//       console.log(res)
//     })
//   })
// })


// 2.第二種方案: Promise中then的傳回值來解決
//requestData("url").then(res => {
//   return requestData(res + "aaa")
// }).then(res => {
//   return requestData(res + "bbb")
// }).then(res => {
//   console.log(res)
// })           

Promise + generator

但是上面的代碼其實看起來也是閱讀性比較差的,有沒有辦法可以繼續來對上面的代碼進行優化呢?第三種方案: Promise + generator實作

♠ 異步處理方案

點選檢視代碼

function requestData(url) {
  // 異步請求的代碼會被放入到executor中
  return new Promise((resolve, reject) => {
    // 模拟網絡請求
    setTimeout(() => {
      // 拿到請求的結果
      resolve(url)
    }, 2000);
  })
}
//但是上面的代碼其實看起來也是閱讀性比較差的,有沒有辦法可以繼續來對上面的代碼進行優化呢?
// 3.第三種方案: Promise + generator實作
function* getData() {
  const res1 = yield requestData("url")
  const res2 = yield requestData(res1 + "aaa")
  const res3 = yield requestData(res2 + "bbb")
  const res4 = yield requestData(res3 + "ccc")
  console.log(res4)
}
// 1> 手動執行生成器函數
const generator = getData()
generator.next().value.then(res => {
  generator.next(res).value.then(res => {
    generator.next(res).value.then(res => {
    
      generator.next(res)
    })
  })
})           

點選檢視代碼

function requestData(url) {
  // 異步請求的代碼會被放入到executor中
  return new Promise((resolve, reject) => {
    // 模拟網絡請求
    setTimeout(() => {
      // 拿到請求的結果
      resolve(url)
    }, 1000);
  })
}
function* getData() {
  const res1 = yield requestData("url")
  const res2 = yield requestData(res1 + "aaa")
  const res3 = yield requestData(res2 + "bbb")
  const res4 = yield requestData(res3 + "ccc")
  console.log(res4)
}

// 2> 自己封裝了一個自動執行的函數
function execGenerator(genFn) {
  //拿到生成器
  const generator = genFn()
  function exec(res) {
    const result = generator.next(res)
    if (result.done) {
      return result.value
    }
    result.value.then(res => {
      exec(res)
    })
  }
  exec()
}

execGenerator(getData)           

我們還可以使用TJ:第三方包co自動執行,const co = require('co');co(getData)

異步函數 async function

本質上是Promise + generator的文法糖點選檢視代碼

function requestData(url) {
  // 異步請求的代碼會被放入到executor中
  return new Promise((resolve, reject) => {
    // 模拟網絡請求
    setTimeout(() => {
      // 拿到請求的結果
      resolve(url)
    }, 2000);
  })
}
// 4.第四種方案: async/await
//*—> async  yield->await
async function getData() {
  const res1 = await requestData("url")
  const res2 = await requestData(res1 + "aaa")
  const res3 = await requestData(res2 + "bbb")
  const res4 = await requestData(res3 + "ccc")
  console.log(res4)
}

getData()           

async關鍵字用于聲明一個異步函數, async是asynchronous單詞的縮寫,異步、非同步;sync是synchronous單詞的縮寫,同步、同時;

點選檢視代碼

async function foo1() {

}
const foo2 = async () => {

}
class Foo {
  async bar() {

  }
}
           

異步函數的内部代碼執行過程和普通的函數是一緻的,預設情況下也是會被同步執行。

異步函數有傳回值時,和普通函數會有差別:

  1. 情況一:異步函數也可以有傳回值,但是異步函數的傳回值會被包裹到Promise.resolve中;
  2. 情況二:如果我們的異步函數的傳回值是Promise,Promise.resolve的狀态會由Promise決定;
  3. 情況三:如果我們的異步函數的傳回值是一個對象并且實作了thenable,那麼會由對象的then方法來決定;

點選檢視代碼

async function foo() {
  console.log("foo function start~")
  console.log("中間代碼~")
  console.log("foo function end~")
  //預設return undefined;
  // 1.傳回一個值
  // 2.傳回thenable
  // return {
  //   then: function(resolve, reject) {
  //     resolve("hahahah")
  //   }
  // }
  // 3.傳回Promise
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      resolve("hehehehe")
    }, 2000)
  })
}
// 異步函數的傳回值一定是一個Promise
const promise = foo()
//當内部return 會執行這個then ,這個then被執行會加入到微任務隊列中
promise.then(res => {
  console.log("promise then function exec:", res)
})           

點選檢視代碼

async function foo() {
  console.log("foo function start~")

  console.log("中間代碼~")

  // 異步函數中的異常, 會被作為異步函數傳回的Promise的reject值的
  throw new Error("error message")

  console.log("foo function end~")
}

// 異步函數的傳回值一定是一個Promise
foo().catch(err => {
  console.log("err:", err)
})

console.log("後續還有代碼~~~~~")           
  1. 通常使用await是後面會跟上一個表達式,這個表達式會傳回一個Promise;
  2. 那麼await會等到Promise的狀态變成fulfilled狀态,之後繼續執行異步函數;
  3. 如果await後面是一個普通的值,那麼會直接傳回這個值;
  4. 如果await後面是一個thenable的對象,那麼會根據對象的then方法調用來決定後續的值;
  5. 如果await後面的表達式,傳回的Promise是reject的狀态,那麼會将這個reject結果直接作為函數的Promise的reject值;

點選檢視代碼

// 1.await跟上表達式
function requestData() {
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      // resolve(222)
      reject(1111)
    }, 2000);
  })
}

// async function foo() {
//   const res1 = await requestData()
//   console.log("後面的代碼1", res1)
//   console.log("後面的代碼2")
//   console.log("後面的代碼3")

//   const res2 = await requestData()
//   console.log("res2後面的代碼", res2)
// }

// 2.跟上其他的值
// async function foo() {
//   // const res1 = await 123
//   // const res1 = await {
//   //   then: function(resolve, reject) {
//   //     resolve("abc")
//   //   }
//   // }
//   const res1 = await new Promise((resolve) => {
//     resolve("why")
//   })
//   console.log("res1:", res1)
// }

// 3.reject值 async function foo(){}是個promise,是以foo().catch
async function foo() {
  const res1 = await requestData()
  console.log("res1:", res1)
}

foo().catch(err => {
  console.log("err:", err)
})