美文网首页
JS中的Promise与Async/Await

JS中的Promise与Async/Await

作者: c小刺猬 | 来源:发表于2020-11-17 18:56 被阅读0次

Promise 的含义

Promise 是异步编程的一种解决方案,比传统的解决方案——回调函数和事件——更合理和更强大。它由社区最早提出和实现,ES6 将其写进了语言标准,统一了用法,原生提供了Promise对象。

从语法上说,Promise 是一个对象,从它可以获取异步操作的消息。

// 传统写法 回调地狱
step1(function (value1) {
  step2(value1, function(value2) {
    step3(value2, function(value3) {
      step4(value3, function(value4) {
        // ...
      });
    });
  });
});

// Promise 的写法
(new Promise(step1))
  .then(step2)
  .then(step3)
  .then(step4);

Promise 对象的状态

Promise 对象通过自身的状态,来控制异步操作。Promise 实例具有三种状态。

  1. 异步操作未完成(pending)
  2. 异步操作成功(fulfilled)
  3. 异步操作失败(rejected)

上面三种状态里面,fulfilled和rejected合在一起称为resolved(已定型)。

这三种的状态的变化途径只有两种。

  1. 从“未完成”到“成功”
  2. 从“未完成”到“失败”

一旦状态发生变化,就凝固了,不会再有新的状态变化。这也是 Promise 这个名字的由来,它的英语意思是“承诺”,一旦承诺成效,就不得再改变了。

创建一个Promise对象

Promise 构造函数

var promise = new Promise(function (resolve, reject) {
  // ... some code

  if (/* 异步操作成功 */){
    resolve(value);
  } else { /* 异步操作失败 */
    reject(new Error());
  }
});

该函数的两个参数分别是resolve和reject。它们是两个函数,由 JavaScript 引擎提供,不用自己部署。

Promise.resolve 方法

将一个普通对象生成一个Promise对象

Promise.resolve()等价于下面的写法。

Promise.resolve('foo')
// 等价于
new Promise(resolve => resolve('foo'))

Promise.reject 方法

生成一个新的 Promise 实例,该实例的状态为rejected。

const p = Promise.reject('出错了');
// 等同于
const p = new Promise((resolve, reject) => reject('出错了'))

p.then(null, function (s) {
  console.log(s)
});
// 出错了

Promise 状态的处理方法

Promise.prototype.then 方法

Promise 实例具有then方法,它的作用是为 Promise 实例添加状态改变时的回调函数。

then方法的第一个参数是resolved状态的回调函数,第二个参数(可选)是rejected状态的回调函数(该参数可以省略)。一旦状态改变,就调用相应的回调函数。

下面是一个Promise对象的简单例子。Promise 新建后就会立即执行。

function timeout(ms) {
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      resolve('done')
    }, ms);
  });
}

timeout(100).then(value => {
  console.log(value); // done
});

then方法返回的是一个新的Promise实例(注意,不是原来那个Promise实例)。因此可以采用链式写法,即then方法后面再调用另一个then方法。

采用链式的then,可以指定一组按照次序调用的回调函数。这时,前一个回调函数,有可能返回的还是一个Promise对象(即有异步操作),这时后一个回调函数,就会等待该Promise对象的状态发生变化,才会被调用。

getJSON("/post/1.json").then(
  post => getJSON(post.commentURL)
).then(
  comments => console.log("resolved: ", comments),
  err => console.log("rejected: ", err)
);

Promise 对象的报错具有传递性,会一直向后传递,直到被捕获为止。如果不设置回调函数,Promise内部抛出的错误,不会反应到外部。比如下面的例子:

p1
  .then(step1)
  .then(step2)
  .then(step3)
  .then(
    console.log,
    console.error
  );

如果step1的状态变为rejected,那么step2和step3都不会执行了(因为它们是resolved的回调函数)。Promise 开始寻找,接下来第一个为rejected的回调函数

Promise.prototype.catch 方法

Promise.prototype.catch()方法是.then(null, rejection)或.then(undefined, rejection)的别名,用于指定发生错误时的回调函数。

p.then((val) => console.log('fulfilled:', val))
  .catch((err) => console.log('rejected', err));

// 等同于
p.then((val) => console.log('fulfilled:', val))
  .then(null, (err) => console.log("rejected:", err));

上面代码中,如果p的状态变为resolved,则会调用then()方法指定的回调函数;如果异步操作抛出错误,状态就会变为rejected,就会调用catch()方法指定的回调函数,处理这个错误。另外,p.then()方法指定的回调函数,如果运行中抛出错误,也会被catch()方法捕获。

一般来说,不要在then()方法里面定义 Reject 状态的回调函数(即then的第二个参数),总是使用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()方法的第二个参数。

Promise.prototype.finally 方法

finally()方法用于指定不管 Promise 对象最后状态如何,都会执行的操作。该方法是 ES2018 引入标准的。

promise
.then(result => {···})
.catch(error => {···})
.finally(() => {···});

上面代码中,不管promise最后的状态,在执行完then或catch指定的回调函数以后,都会执行finally方法指定的回调函数。

Nodejs中的Promise

nodejs8以上已经原生支持es6语法书写代码了,虽然 Promise 已经普及,但是 Node.js 里仍然有大量的依赖回调的异步函数,

所以 Node8 就提供了 util.promisify() 这个方法,方便我们快捷的把原来的异步回调方法改成返回 Promise 实例的方法

// 异步回调形式
fs.readFile('./test.js',function(err, data){
    console.log(data)
})

// promisify 形式
const readFileAsync = Promise.promisify(fs.readFile)

readFileAsync('./test.js').then(function(data){
    console.log(data)
}).catch(err){
    console.log(err)
}

Async/Await

ES2017(ES8) 标准引入了 async 函数,使得异步操作变得更加方便。async/await使得异步代码看起来像同步代码,这正是它的魔力所在。

async/await是基于Promise实现的,它不能用于普通的回调函数。

基本用法

async

async函数返回一个 Promise 对象。

async函数内部return语句返回的值,会成为then方法回调函数的参数。

async function f() {
  // 等同于
  // return 123;
  return await 123;
}

f().then(v => console.log(v))
// 123

async函数内部抛出错误,会导致返回的 Promise 对象变为reject状态。抛出的错误对象会被catch方法回调函数接收到。

async function f() {
  throw new Error('出错了');
}

f().then(
  v => console.log('resolve', v),
  e => console.log('reject', e)
)
//reject Error: 出错了

await

await命令后面是一个 Promise 对象,返回该对象的结果。如果不是 Promise 对象,就直接返回对应的值。

await只能用在async方法内部。

async function f() {
  let result = await Promise.resolve('hello world');
  console.log(result) // hello world
}

Promise与async/await

promise改造为async/await的写法:

// promise
const stat = util.promisify(fs.stat);
stat('.')
 .then((stats) => {
  // Do something with `stats`
 })
 .catch((error) => {
  // Handle the error.
 });

// async/await
const stat = util.promisify(fs.stat);
async function readStats(dir) {
 try {
  let stats = await stat(dir);
  // Do something with `stats`
 } catch (err) { // Handle the error.
  console.log(err);
 }
}
readStats('.');

相关文章

网友评论

      本文标题:JS中的Promise与Async/Await

      本文链接:https://www.haomeiwen.com/subject/pulfiktx.html