[ES6] Promise.then()使用小结

作者: TAIS3 分类: 杂七杂八 发布时间: 2021-11-19 09:09

promise简介

  • 为了解决回调地狱嵌套,在EcmaScript6中新增了一个API:Promise。Promise是一个构造函数。Promise是一个容器,Promise本身不是异步的,但是容器内部往往存放一个异步操作
  • Promise构造函数接受一个函数作为参数,该函数的两个参数分别是resolvereject。resolve和reject两个形参接收的是函数,接受的函数由 JavaScript 引擎提供,不用自己部署。
  • 通过new Promise构造器创建的promise对象是有状态的,状态可能是pending(进行中),resolved(已完成,也称为fullfilled),rejected(已失败)中的一种。新创建的promise对象的状态是pending,后续可以变为resolved或rejected,状态一旦改为resolved或rejected,就不会再变了。
  • resolve函数调用后会将Promise对象的状态从“未完成”变为“成功”(即从 pending 变为 resolved),在异步操作成功时调用,并将异步操作的结果,作为参数传递出去;
  • reject函数调用后将Promise对象的状态从“未完成”变为“失败”(即从 pending 变为 rejected),在异步操作失败时调用,并将异步操作报出的错误,作为参数传递出去。

例一:

const promise = new Promise(function(resolve, reject) { // resolve 和reject这两个形参是由 JavaScript 引擎传进来的,是两个函数
  // 这里进行一些异步操作代码
    
  if (/* 异步操作成功 */){
    resolve(value); // resolve函数的作用是将这个promise对象的状态从 pending 变为 resolved。并将异步操作的结果value,作为参数传递出去。
    console.log(1)
  } else /* 异步操作失败 */{
    reject(error); // reject函数的作用是将这个promise对象的状态从 pending 变为 rejected。并将错误传递出去。
  }
});
  • Promise实例生成以后,可以用then方法指定两个回调函数。
  • then方法中的第一个参数,是promise对象的状态由pending变为resolved后会调用的回调函数;then方法中的第二个参数,是promise对象的状态由pending变为rejected后调用的回调函数;
  • 这两个回调函数中,rejectedCallback可选的,不一定要提供。
  • 例一中,如果异步操作成功,就会调用resolve(value),调用resolve(value),当前promise的状态由进行中变为成功,随即调用then方法中的第一个回调函数参数。
  • 我们在例一中调用resolve(value)的时候其实就可以看作是在调用then方法里传的第一个回调函数。
  • then方法指定的回调函数,将在当前脚本所有同步任务执行完才会执行( 也就是说resolve()后面如果还有其他同步语句(如上面例子中的console.log(1) ),会先执行完这些同步语句, 然后在执行then方法中的回调函数)。(其实这里涉及到的是事件循环的概念,不清楚的可以去了解)
promise.then(resolvedCallback, rejectedCallback);
promise.then(function(value) {
  // 把这个函数作为promise对象状态变为成功后调用的回调函数
  // 成功时的逻辑
}, function(error) {
  // 把这个函数作为promise对象状态变为失败后调用的回调函数
  // 失败时的逻辑
});

例二:

  • 下面的代码中,首先利用Promise构造器创建了一个promise对象,并且调用了then方法指定了该promise对象的状态由pending变为resolved后会调用的回调函数。
  • promise里的异步操作是一个定时操作,当计时到了后,调用了resolve()函数(也就是调用了我们在then方法中写的第一个回调函数),该函数调用后,promise对象的状态由pending变为resolved。
new Promise((resolve, reject) => {
  setTimeout(function () {
    resolve() ;
  }, 1000)
}).then((data) => { // 因为new Promise返回的就是一个promise对象实例,所以这里可以链式操作
  alert('成功的回调');
})

promise容器中的执行顺序

new Promise((resolve, reject) => {
  setTimeout(function () {
    console.log('时间到了')
    resolve('11') 
    console.log('22')
  }, 1000)
  console.log('33')
}).then((data) => {
  console.log(data)
})
console.log('hhh')
// 打印顺序:
// 33
// hhh
// 时间到了
// 22
// 11
  • promise本身不是异步的,只是内部包含了异步代码。
  • 首先启动定时器(但是不会执行定时器内部的回调函数)
  • 因为setTimeout是一个异步代码,不会影响后续代码的继续执行,所以随即执行 console.log(‘33’)
  • new Promise部分就执行完了,然后按顺序执行 console.log(‘hhh’)
  • 当定时到了后,会执行定时器中的回调函数,依次执行 console.log(‘时间到了’) resolve(‘11’) (实际上可以看作执行then方法传过去的回调函数) console.log(‘22’)
  • 按照上面的说法应该先打印11,在打印22呀?这是为什么呢?
  • 这就是前面提到的 then方法指定的回调函数,将在当前脚本所有同步任务执行完才会执行, 也就是说console.log(22)会先打印。

resolve函数和reject函数中传递的参数

  • 如果调用resolve函数和reject函数时带有参数,那么它们的参数会被传递给回调函数。reject函数的参数通常是Error对象的实例,表示抛出的错误;resolve函数的参数除了正常的值以外,还可能是另一个 Promise 实例。

如果resolve或reject中的参数是一个正常的值

例三:

// 1. 创建Promise实例对象
var p1 = new Promise(function (resolve, reject) {
  setTimeout(function () {
    // 调用resolve函数后p1的状态pending->fulfilled
    // 然后就会调用成功的回调函数,并将这个参数1传递给回调函数
    resolve(1); 
  }, 1000);
})

// 2. 通过then方法指定成功和失败时的回调函数
p1
  .then(function (data) {
    // 成功的回调函数 (在定时器中调用resolve函数后 就会调用当前这个回调函数)
    console.log("data", data)
  }, function (err) {
    // 失败的回调
    console.log("err", err)
  })
// 打印顺序
// 1s后打印'data 1'
var p1 = new Promise(function (resolve, reject) {
  setTimeout(function () {
    reject(1); // 调用reject函数后,p1的状态pending->rejected
  }, 1000);
})

p1
  .then(function (data) {
    console.log("data", data)
  }, function (err) {
    console.log("err", err)
  })
// 打印顺序
// 1s后打印'err 1'

如果resolve或reject中的参数是promise实例对象

var p1 = new Promise(function (resolve, reject) {
  setTimeout(function () {
    resolve('1');
  }, 3000);
})
var p2 = new Promise(function (resolve, reject) {
  setTimeout(function () {
    resolve(p1); // resolve的参数是一个promise对象
  }, 1000);
})

p2
  .then(function (data) {
    console.log('resolve执行啦')
    console.log(data) 
  }, function (err) {
    console.log(err)
  })
  // 打印顺序
  // 3s后依次打印 'resolve执行啦'  '1'
  • 上面代码中,p1和p2都是 Promise 的实例,但是p2的resolve方法将p1作为参数。
  • 注意,这时p1的状态就会传递给p2,也就是说,p1的状态决定了p2的状态。如果p1的状态是pending,那么p2的回调函数就会等待p1的状态改变;如果p1的状态已经是resolved或者rejected,那么p2的回调函数将会立刻执行。

Promise.prototype.then()

  • then方法会返回一个新的Promise实例。因此可以采用链式写法,即then方法后面再调用另一个then方法。采用链式的then,可以指定一组按照次序调用的回调函数。

成功时的回调 和 失败时的回调函数 中的return返回值

  • 第一个回调函数完成以后,会将返回结果作为参数,传入下一级的回调函数。返回结果可能是一个参数或者一个新的promise实例(即有异步操作),这时候一个回调函数,就会等待该Promise对象的状态发生变化,才会被调用。
  • 如果then方法中的回调函数(不管是成功的回调还是失败的回调)返回了一个参数(return xxx),那么这个then方法返回的新的promise的状态会变成fulfilled(同时成功的回调函数也会被调用)。

如果返回值为一个普通参数

var p1 = new Promise(function (resolve, reject) {
  setTimeout(function () {
   // 执行resolve函数后p1的状态 pending->fulfilled
   // 然后执行p1成功时的回调函数 并传递参数
    resolve('1'); 
  }, 1000);
})

var p2 = p1.then(function (data) { // p1成时的回调函数
  console.log("data", data);
  return 'hello p2'; // 执行return语句后p2的状态pending->fulfilled 同时p2成功的回调函数会被调用
  // 当前函数中 return 的结果就可以在后面的 then 中 function 接收到
}, function (err) { // p1失败时的回调函数
  console.log("err", err);
  return 'hello p2 nihao';
}); // 当前then返回一个新的promise:p2

var p3 = p2.then(function (data) {  // p2成功时的回调函数
  console.log("data", data);
}, function (err) { // p2失败时的回调函数
  console.log("err", err);
})
// 打印顺序
// 1s后打印'data 1'
// 紧接着打印'data hello p2'

写成链式调用的形式:

var p1 = new Promise(function (resolve, reject) {
  setTimeout(function () {
    resolve('1');
  }, 1000);
})

p1
.then(function (data) {
  console.log("data", data);
  return 'hello p2';
}, function (err) {
  console.log("err", err)
  return 'hello p2 nihao';
})
.then(function (data) {
  console.log("data", data);
}, function (err) {
  console.log("err", err);
})
var p1 = new Promise(function (resolve, reject) {
  setTimeout(function () {
    reject('1'); // 执行resolve函数后p1的状态pending->rejected 失败的回调
  }, 1000);
})

var p2 = p1.then(function (data) {
  console.log("data", data);
  return 'hello p2'; 
}, function (err) {
  console.log("err", err)
  return 'hello p2 nihao'; // 执行return语句后p2的状态pending->fulfilled
  // 可以看出不管是成功的回调还是失败的回调中的return 但会导致当前then方法返回的promise变为成功状态
}); // 当前then返回一个新的promise:p2

p2.then(function (data) {
  console.log("data", data);// 这个回调会被调用
}, function (err) {
  console.log("err", err);
})
// 打印顺序
// 1s后打印'err 1'
// 紧接着打印'data hello p2 nihao'
  • 从上面的例子可以总结出:如果then方法中返回一个参数(return xxx),那么这个then方法返回的新的promise的状态会变成fulfilled。

如果返回值为一个新的promise实例

var time1, time2
var p1 = new Promise(function (resolve, reject) {
  setTimeout(function () {
    // 1s后进入这里
    // 执行resolve函数后p1的状态 pending->fulfilled
   	// 然后执行p1成功时的回调函数 并传递参数
    resolve('1');
  }, 1000);
})

var p2;
p1.then (function (data) { // p1成功时的回调函数
  time1 = Date.now()
  console.log("data", data);
  p2 = 
  new Promise(function (resolve, reject) {
    setTimeout(function () {
     // 执行resolve函数后p2的状态 pending->fulfilled
   	 // 然后执行p2成功时的回调函数 并传递参数
      resolve('2');
    }, 3000);
  });
  // return 一个 Promise 对象的时候,当前then()方法的返回值就是这个p2对象
  return p2;
}, function (err) { // p1失败时的回调函数
  console.log("err", err)
})
.then (function (data) { // p2成功时的回调函数
  time2 = Date.now()
  console.log(time2-time1)
  console.log("data", data);
}, function (err) { // p2失败时的回调函数
  console.log("err", err);
})

// 打印顺序
// 1s后打印'data 1'
// 再过3s后依次打印'3001' 和 'data 2'

写成链式的

var p1 = new Promise(function (resolve, reject) {
  setTimeout(function () {
    resolve('1');
  }, 1000);
})
p1.then (function (data) {
  console.log("data", data);
  return new Promise(function (resolve, reject) {
    setTimeout(function () {
      resolve('2');
    }, 3000);
  });
}, function (err) {
  console.log("err", err)
})
.then (function (data) {
  console.log("data", data);
}, function (err) {
  console.log("err", err);
})
// 打印顺序
// 1s后打印'data 1'
// 再过3s后依次打印'3001' 和 'data 2'

成功时的回调 和 失败时的回调函数 中抛出异常

  • 如果then中抛出异常,那么这个then返回的新的promise实例的状态会被置为rejected
var p1 = new Promise(function (resolve, reject) {
  setTimeout(function () {
    // p1状态pending->fulfilled
    // 调用p1成功时的回调函数
    resolve('1');
  }, 1000);
})

var p2 = p1.then (function (data) { // p1成功时的回调
  console.log("data", data);
  throw new Error('test1'); // 抛出异常后p2状态pending->rejected
}, function (err) { // p1失败时的回调
  console.log("err", err);
  throw new Error('test2');
})

p2.then (function (data) { // p2成功时的回调
  console.log("data", data);
}, function (err) { // p2失败时的回调
  console.log("err", err);
})
// 打印顺序
// 1s后打印'data 1'
// 紧接着打印'err Error: test1'
var p1 = new Promise(function (resolve, reject) {
  setTimeout(function () {
    // p1状态pending->rejected
    // 调用p1失败时的回调函数
    reject('1');
  }, 1000);
})

var p2 = p1.then (function (data) { // p1成功时的回调
  console.log("data", data);
  throw new Error('test1'); 
}, function (err) { // p1失败时的回调
  console.log("err", err);
  throw new Error('test2'); // 抛出异常后p2状态pending->rejected
})

p2.then (function (data) { 
  console.log("data", data);
}, function (err) { // p2状态变为失败后调用这个回调
  console.log("err", err);
})
// 打印顺序
// 1s后打印'err 1'
// 紧接着打印'err Error: test2'

Promise.prototype.catch()

  • Promise.prototype.catch()方法是.then(null, rejection)或.then(undefined, rejection)的别名,用于指定发生错误时的回调函数。
var p1 = new Promise(function (resolve, reject) {
  setTimeout(function () {
    resolve('1');// 1.调用resolve后p1的状态pending->fulfilled
  }, 1000);
})

var p2 = p1.then (function (data) { // p1状态变为成功后调用这个回调
  console.log("data", data); 
  throw new Error('test1'); // 抛出异常后p2状态pending->rejected
}, function (err) {
  console.log("err", err);
  throw new Error('test2'); 
})

p2.catch (function (err) { // p2状态变为失败后调用这个回调
  console.log("err", err);
})
// 打印顺序
// 1s后打印'data 1'
// 紧接着打印'err Error: test1'
var p1 = new Promise(function (resolve, reject) {
  setTimeout(function () {
    resolve('1');// 1.调用resolve后p1的状态pending->fulfilled
  }, 1000);
})
p1.then (function (data) { // p1状态变为成功后调用这个回调
  console.log("data", data);
  return new Promise(function (resolve, reject) {
    setTimeout(function () {
      reject('2'); // 2.调用reject后p2的状态pendging->rejected
    }, 3000);
  });
}, function (err) {
  console.log("err", err)
})
.catch (function (err) { // p2状态变为失败后调用这个回调
  console.log("err", err);
})
// 打印顺序
// 1s后打印'data 1'
// 紧接着打印'err 2'

参考文章

https://es6.ruanyifeng.com/#docs/promise
https://segmentfault.com/a/1190000011652907

文章来源于互联网:[ES6] Promise.then()使用小结

发表回复