打开APP
userphoto
未登录

开通VIP,畅享免费电子书等14项超值服

开通VIP
JavaScript异步之从promise到await

一、从回调到Promise,为什么要使用它

当我们在执行异步任务的时候,往往会使用一个回调函数,它会在异步任务结束时被调用,它有一个共同点就是会在未来被执行。例如在node.js中异步读取文件:

fs.readFile('/etc/passwd', (err, data) => {  if (err) throw err;  console.log(data);});

又如我们写了个ajax请求函数:

// 简化版,请忽略细节const request = function(callback){  let xhr = new XMLHttpRequest();   // .....  xhr.onreadystatechange = function(){    callback(xhr)                }}//调用request(function(xhr){  if (xhr.readyState === 4 && xhr.status === 200) {      // ....  } else {      //....  }  })

甚至是一个定时器任务:

const doSomethingLater = function(delay, callback){    setTimeout(callback, delay*1000)}// 调用doSomethingLater(1, function(){  // .....})

这样看使用回调的方式似乎没什么问题,但是当回调函数中又有异步任务时,就可能出现多层回调,也就是回调地狱的问题。多层回调降低了代码的可读性和可维护性。

Promise为我们做了什么

简单来讲,Promise将异步任务包装成对象,将异步任务完成后要执行的函数传给then方法,通过resolve来调用该函数。如上面定时器任务可以改写成:

const doSomethingLater = function(delay){    return new Promise((resolve)=>{      setTimeout(()=>{ resolve() }, delay*1000)    })}doSomethingLater(1)    .then(()=>{      console.log('任务1')    })

如果定时任务中又执行定时任务,就可以这样写,而不是再嵌套一层回调:

doSomethingLater(1)    .then(() => {        console.log('任务1')        return doSomethingLater(1)    })    .then(() => {        console.log('任务2')    })

Promise的作用:

  • 把异步任务完成后的处理函数换个位置放:传给then方法,并支持链式调用,避免层层回调。
  • 捕获错误:不管是代码错误还是手动reject(),都可以用一个函数来处理这些错误。

二、你可能不知道的Promise细节

用了Promise就是异步代码

就算你的代码原来没有异步操作:

Promise.resolve()    .then(() => {        console.log(1)    })console.log(2)// 2// 1

这一点可以查一下事件循环相关知识

catch的另一种写法
Promise.reject('error')    .then(() => {    })    .catch((err) => {        console.log(err)    })// 等价于Promise.reject('error')    .then(() => {    })    .then(null, (err) => {        console.log(err)    })// 或者Promise.reject('error')    .then(() => {    }, (err) => {        console.log(err)    })

其实catch只是个语义化的语法糖,我们也可以直接用then来处理错误。

then 或 catch 方法始终返回promise对象

then方法第一个参数和第二个参数(或catch的参数),只是调用条件不同。

Promise.resolve()    .then(() => {        return 1    })    .then((res) => {        console.log(res) // 1    })    Promise.resolve()    .then(() => {       // 不返回什么    })    .then((res) => {        console.log(res) // undefined,因为函数默认返回undefined     })   

如果是返回一个promise对象:

Promise.resolve()    .then(() => {        return new Promise((resolve) => {            resolve(2)        })    })    .then((res) => {        console.log(res) // 2, 根据返回的那个promise对象的状态来    })

我们可以通过包装,使一个promise对象的最后状态始终是成功的:
例如:

const task = () => {    return new Promise((resolve, reject) => {        // ....    })}task()    .then((res) => {        console.log(res)    })    .catch((err) => {        console.log(err)    })

原本调用task函数时需要在外面加一层catch捕获错误,其实可以包装一下:

const task = () => {    return new Promise((resolve, reject) => {        // ....    })        .then((res) => {            return {                status: 'success',                value: res            }        })        .catch((err) => {            return {                status: 'fail',                value: err            }        })}// 现在调用就会更简洁!task()    .then((result) => {        console.log(result)    })

catch中报错也可以在后面继续捕获,因为catch也是返回promise对象嘛

Promise.reject('first error')    .catch((err) => {        console.log(err)        throw new Error('second error')    })    .then(null, (err) => {        console.log(err)    })

三、await:新的语法糖

await使得异步代码更像是同步代码,对串行的异步调用写起来更自然。await后面跟一个值或promise对象,如果是一个值,那么直接返回。如果是一个promise对象,则接受promise对象成功后的返回值。或者在await后面调用一个async函数

const task = async () => {    return new Promise((resolve, reject) => {        resolve(1)    })}const handle = async () => {    let value = await task()    console.log(value)}handle() // 1

使用await要注意错误的捕获,因为await只关心成功

const task = async () => {    return new Promise((resolve, reject) => {        reject(1)    })}const handle = async () => {    let value = await task()    console.log(value)}handle()

这样写会报错,所以要么在task函数中捕获错误,要么就在task调用时捕获,像这样:

const task = async () => {    return new Promise((resolve, reject) => {        reject(1)    })}const handle = async () => {    let value = await task().catch((err) => { console.log(err) })    console.log(value) // undefine, 因为错误处理函数没返回值,你也可以返回错误信息,这样就会通过value拿到}

需要注意的是,使用await也会使代码变成异步的:

const handle = async () => {    let value = await 1    console.log(value)}handle()console.log(2)// 2// 1

完~

本站仅提供存储服务,所有内容均由用户发布,如发现有害或侵权内容,请点击举报
打开APP,阅读全文并永久保存 查看更多类似文章
猜你喜欢
类似文章
【热】打开小程序,算一算2024你的财运
目前最好的 JavaScript 异步方案 async/await
async/await 是如何让代码更加简洁的?
ES6新特性(5)之Promise/async
异步函数与同步函数
[译]理解Javascript的异步等待
JavaScript异步编程
更多类似文章 >>
生活服务
热点新闻
分享 收藏 导长图 关注 下载文章
绑定账号成功
后续可登录账号畅享VIP特权!
如果VIP功能使用有故障,
可点击这里联系客服!

联系客服