promise和async

promise和async

promise

我觉得脱离promise去谈async是不全面的,理解promise对学习和使用async具有很大的意义。直接看一个阮一峰上Promise对象实现Ajax操作的实例,看着实例去解析:

const getJSON = function(url) {
  const promise = new Promise(function(resolve, reject){
    const handler = function() {
      if (this.readyState !== 4) {
        return;
      }
      if (this.status === 200) {
        resolve(this.response);
      } else {
        reject(new Error(this.statusText));
      }
    };
    const client = new XMLHttpRequest();
    client.open("GET", url);
    client.onreadystatechange = handler;
    client.responseType = "json";
    client.setRequestHeader("Accept", "application/json");
    client.send();

  });

  return promise;
};

getJSON("/posts.json").then(function(json) {
  console.log('Contents: ' + json);
}, function(error) {
  console.error('出错了', error);
});

promise是个对象实例,它有3种状态,pending(进行中)、resolved(已成功)、rejected(已失败),这个对象接受一个带有2个函数参数的函数参数a,这两个函数参数是resolve和reject,在a中调用它们就可以改变这个promise对象的状态,我们看到例子中根据请求的返回状态来决定是调用resolve还是reject,promise的状态确定了之后就是不可逆的了,之后的操作就交给了then函数,then接受两个函数对象,分别来处理resolved状态和rejected状态的情况。

promise.then(function(value) {
  // success
}, function(error) {
  // failure
});

为了强化理解,我们在看一个阮的例子:

const p1 = new Promise(function (resolve, reject) {
  setTimeout(() => reject(new Error('fail')), 3000)
})

const p2 = new Promise(function (resolve, reject) {
  setTimeout(() => resolve(p1), 1000)
})

p2
  .then(result => console.log(result))
  .catch(error => console.log(error))
  // Error: fail

这里我们可以看到两个promise对象,分别在所有脚本执行完后的1s和3s改变状态,p1作为p2的resolve参数,也就是p2需要等待p1的状态发生改变,1s之后p2就会执行resolve,返回p1,p1的状态还没确定,所以就需要等p1的状态确定,2s后p1返回rejected状态,触发catch。

一般来说,调用resolve或reject以后,Promise 的使命就完成了,后继操作应该放到then方法里面,而不应该直接写在resolve或reject的后面。所以,最好在它们前面加上return语句,这样就不会有意外。

new Promise((resolve, reject) => {
  return resolve(1);
  // 后面的语句不会执行
  console.log(2);
})

catch()也是一个重要的对象方法,一段健壮的代码需要有捕获错误的处理逻辑,catch方法其实也就是.then(null, rejection)的别名,如果promise对象状态变为rejected,就会调用catch方法指定的回调函数,

// 写法一
const promise = new Promise(function(resolve, reject) {
  try {
    throw new Error('test');
  } catch(e) {
    reject(e);
  }
});
promise.catch(function(error) {
  console.log(error);
});

// 写法二
const promise = new Promise(function(resolve, reject) {
  reject(new Error('test'));
});
promise.catch(function(error) {
  console.log(error);
});

reject其实就是等价于抛出错误。catch会处理之前所有promise产生的错误,所以我们就不需要在then方法中在定义rejected状态的回调函数,在最后使用catch方法就可以了:

// bad
promise
  .then(function(data) {
    // success
  }, function(err) {
    // error
  });

// good
promise
  .then(function(data) { //cb
    // success
  })
  .catch(function(err) {
    // error
  });

上面代码中,第二种写法要好于第一种写法,理由是第二种写法可以捕获前面then方法执行中的错误,也更接近同步的写法(try/catch)。因此,建议总是使用catch方法,而不使用then方法的第二个参数。

再回到之前的实例,js自带的ajax请求已经比较少用了,我们采用fetch的方式再实现一下(fetch就是基于promise实现的,具体用法参考MDN-fetch的使用):

const getJSON = function(url) {
    return new Promise(function(resolve,reject){
        fetch(serverUrl)
        .then(response => response.json())
        .then(json => resolve(json))
        .catch(ex => reject(ex))
    });
};

getJSON("/posts.json").then(json=>{
  console.log('Contents: ' + json);
}).catch(error=>{
  console.error('出错了', error);
});

这就是promise的一个简单用法,到这边都理解了,之后的all,race什么的拓展,即看即用就可以了。

async

有时候非常痛恨这样一种感觉,当你想要了解一个概念,发现就需要去了解另一个概念,要了解另一个概念,还得追溯其它的知识,最后等你真正理解了,发现看的东西都差不多占了半本书了,好处就是无形之中学了很多东西,坏处就是达成目的所耗费的时间大大增加,实在令人抓狂,当初了解promise的时候都是从异步,回调,并发这些东西一个个看起的,同样,了解async,还需要除了这些之外的generator,,虽然async,await是generator的一个语法糖,但是我觉得要去使用async,并不一定需要了解generator,两者在使用上可以割裂开来,所以我就不花篇幅写这个了。。。

async是我们定义函数时的一个修饰词,当用async定义函数时,表示我们的函数体内有些操作不是立即能执行完的–异步的,就比如异步请求:

async function getStockPriceByName(name) {
  const symbol = await getStockSymbol(name);
  const stockPrice = await getStockPrice(symbol);
  return stockPrice;
}

getStockPriceByName('goog').then(function (result) {
  console.log(result);
});

getStockSymbol和getStockPrice就是async函数中的一部操作,then中需要执行的操作就需要等到这两个异步请求都完成并且返回stockPrice这个promise对象后才可以执行。

如果await返回的是rejected,那么then就没办法执行了,所以我们需要用try catch 包裹await语句,当然最好还是用一下方式:

async function myFunction(){
  await somethingThatReturnAPromise().catch(function (err){
    console.log(err);
  })
}

面对多个请求并发的情况,同样可以使用Promise.all来处理:

  async function dbFunction(db){
    let docs = [{},{},{}];
    let promises = docs.map((doc) => db.post(doc));
    let results = await Promise.all(promises);
    console.log(results);
  }

最后,我们不妨用async的方式把最初的函数改造一下,我们把最终的处理请求结果的函数封装起来:

const getJSON = function(url) {
    return new Promise(function(resolve,reject){
        fetch(serverUrl)
        .then(response => response.json())
        .then(json => resolve(json))
        .catch(ex => reject(ex))
    });
};

getJSON("/posts.json").then(json=>{
  console.log('Contents: ' + json);
}).catch(error=>{
  console.error('出错了', error);
});

/***************************async******************************/

async getResultData(url)=>{
  const resultData = await getJSON(url).catch((err)=>{
    console.log(err)
  })
  return resultData;
}

getResultData('/posts.json').then((resultData)=>{
  console.log('Content:' +json)
})

直观当然是直观一些,但是不知道我这种写法是不是写烦了,还是可以直接写成下面的形式:

async getResultData(url)=>{
  const resultData = await getJSON(url);
  return resultData;
}
getResultData('/posts.json').then((resultData)=>{
  console.log('Content:' +json)
}).catch((err)=>{
  console.log(err)
})

欢迎指正。

猜你喜欢

转载自blog.csdn.net/YPJMFC/article/details/80489669