Loading

♠ 异步处理方案

♣ 异步处理方案

需求:

  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中抛出了异常,那么程序它并不会像普通函数一样报错,而是会作为Promise的reject来传递;

点击查看代码
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("后续还有代码~~~~~")

async函数另外一个特殊之处就是可以在它内部使用await关键字,而普通函数中是不可以的。

await关键字有什么特点呢?

  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)
})
posted @ 2021-11-02 21:54  sunflower-js  阅读(40)  评论(0编辑  收藏  举报