ES6的学习笔记(十)Promise

时间:2021-2-20 作者:admin

Promise

1.Promise的含义

Promise是异步编程的解决方案,比回调函数和事件更强大和合理。
Promise,一个容器,保存着某个未来才会结束的事件(通常是异步操作)的结果。
语法上,Promise就是一个对象,可以获取异步操作结束后的消息。
Promise的两个特点:

  • 对象的状态不受外界影响。Promise代表一个异步操作,具有三种状态:pending(进行中)、fulfilled(已完成)、rejected(已失败)。只有异步操作的结果可以决定装态,其他任何操作都不行。
  • 状态改变后就不会再变了,且任何时候都可以得到这个结果。Promise对象状态的改变有两种可能:从pending到fulfilled和从pending到rejected。只要状态发生,就凝固了,一直保持这个结果。即,resolved(已定型)。

Promise的缺点:
无法取消,一旦新建,立即执行。
若不设置回调函数,Promise内部抛出的错误无法反应到外部。
当处于pending状态时,无法得知目前进展到哪个阶段(刚开始还是即将完成。)

2.基本用法

Promise对象是一个构造函数,用来生成Promise实例。

let promise = new Promise((resolve,reject) => {
   // some code 
   if(/*异步操作成功*/true){
      resolve('操作成功')
      return
   }
   reject('操作失败')
})

Promise构造函数接受一个函数作为参数,该函数的参数是resolve和reject两个函数,这两个函数由JavaScript引擎提供。
resolve函数:将Promise对象的状态从未完成变为成功(pending–>resolved),并将成功之后要传递的结果作为参数抛出去。
reject函数:将Promise对象的状态从未完成变为失败(pending–>rejected),并将异步操作失败后报出的错误作为参数传递出去。
实例生成后,用then方法指定resolved状态和rejected状态的回调函数。

// 接上述promise
 promise.then((value) => {
     console.log(value);
 },(error) => {
     console.log(error);
 })
// 第一个函数在Promise变为resolved时调用,第二个函数在Promise变为rejected时调用,第二个函数可以省略。两个函数都接受Promise对象抛出的值作为参数。

Promise新建后立即执行。

new Promise((resolve, reject) => {
    console.log("object");
    resolve('resolve')
}).then((result) => {
    console.log(result);
})
console.log("Hi!");
// object
// Hi
// resolve

reject函数的参数通常是Error对象的实例,表示抛出的错误。resolve函数的参数除了正常的值外,还可能是另一个Promise实例。

let p1 = new Promise((resolve, reject) => {
   // ...
});
let p2 = new Promise((resolve, reject) => {
   resolve(p1)
});

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))
  //上面代码中,p1是一个 Promise,3 秒之后变为rejected。
  //p2的状态在 1 秒之后改变,resolve方法返回的是p1。由于p2返回的是另一个 Promise,导致p2自己的状态无效了,由p1的状态决定p2的状态。
  //所以,后面的then语句都变成针对后者(p1)。又过了 2 秒,p1变为rejected,导致触发catch方法指定的回调函数。

调用resolve和reject后,并不会终结Promise参数函数的执行。

new Promise((resolve, reject) => {
    resolve(1)
    console.log(2);
}).then((result) => {
    console.log(result);
})
// 2
// 1

一般而言,调用resolve或reject之后,Promise的任务就结束了,后续的操作应该放在then方法里面。所以,最好加上return语句。

new Promise((resolve, reject) => {
    return resolve('value')
    console.log("object"); // 这一句不会执行
});

Promise.prototype.then()

Promise的实例具有then方法,作用是为Promise实例添加状态改变时的回调函数。
then方法第一个参数是resolved状态的回调函数,第二个参数(可选)是rejected状态的回调函数。
then方法返回新的Promise实例,因此可以采用链式写法。

new Promise((resolve, reject) => {
    return resolve('promise1')
}).then(res => {
    console.log(res);
    return 'promise2';
})
    .then(res => {
        console.log(res)
    })
// 第一个then中回调函数返回的结果,会作为参数传入第二个then中的回调函数。

使用链式的then,可以指定一组按照次序调用的回调函数。若前一个回调函数返回的是Promise对象,则后一个回调函数则会等待Promise对象的状态发生变化后,才会被调用。

 new Promise((resolve, reject) => {
    if (true) {
        return resolve('my value')
    }
    reject('my error')
}).then((params) => {
    return new Promise((resolve, reject) => {
        if (true) {
            return resolve('output value')
        }
        reject('error')
    })
}).then((result) => {
    console.log(result);
}).catch((error) => {
    console.log(erro)
})
// 第二个then方法指定的回调函数会等待新Promise对象的状态变化,若变为resolved,则调用第一个回调函数,若变为rejected,则调用第二个回调函数。

Promise.prototype.catch()

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

new Promise((resolve, reject) => {
     // dosomething  
 }).then((params) => {
     // dosomething
 }).catch((error) => {
     console.log(error)
 })
 // 第一个Promise中reject的错误和then回调函数中抛出的错误都会被catch()方法所捕获。

Promise对象的错误具有冒泡性质,会一直向后传递,直到被捕获。即,错误总会被下一个catch()语句所捕获。

new Promise((resolve, reject) => {
   // dosomething
}).then((params) => {
   // dosomething
}).then((params) => {
   // dosomething
}).catch((params) => {
   // dosomething
})
// 三个Promise对象中任意一个所产生的的错误都会被catch()方法所捕获。

一般而言,不建议在then()方法里定义rejected状态的回调函数(即第二个参数),而总是使用catch()方法捕获错误。

// bad
new Promise((resolve, reject) => {

}).then((res) => {

},(err) => {

})
// good
new Promise((resolve, reject) => {

}).then((result) => {

}).catch((err) => {

});

没有使用catch()方法指定错误处理的回调函数,Promise对象抛出的错误就不会传递到外层,即不会有任何反应。

 new Promise((resolve, reject) => {
   resolve(x+33)
}).then((result) => {
   console.log(result);
})
let timer = setTimeout(() => {
   console.log("object");
}, 2000);
// 上述代码在2s后还是会打印object,不会因为报错而停止执行,即Promise会吃掉错误。

catch()方法返回的还是一个Promise对象,后面可以继续接then()方法。
如果Promise对象没有抛出错误,则会跳过catch()方法。

new Promise((resolve, reject) => {
    resolve(s+333)
}).catch((err) => {
    console.log(err)
}).then((res) => {
    console.log('end')
})
// ReferenceError: s is not defined

// end

catch()方法之中,也能再抛出错误。

someAsyncThing().then(function() {
  return someOtherAsyncThing();
}).catch(function(error) {
  console.log('oh no', error);
  // 下面一行会报错,因为y没有声明
  y + 2;
}).catch(function(error) {
  console.log('carry on', error);
});
// oh no [ReferenceError: x is not defined]
// carry on [ReferenceError: y is not defined]
// 第一个catch()方法中的错误被第二个catch()方法所捕获。

Promise.prototype.finally()

finally()方法是不管最后Promise对象的状态如何,都会执行的操作。finally()方法的回调函数不接受任何参数,即表明finally方法里面的操作是与状态无关的,与Promise的执行结果没有关系。

new Promise((resolve, reject) => {

}).then((result) => {

}).catch((err) => {

}).finally(()=> {

})

finally()方法本质上是then()方法的特例。

new Promise((resolve, reject) => {
    // dosomething
}).finally(() => {
    // code
})
// 等同于
new Promise((resolve, reject) => {
    // dosomething
}).then((res) => {
    // code
    return res
},(err) => {
    // code
    throw err
})

finally()方法的实现。

Promise.prototype.finally = function (callback) {
  let P = this.constructor;
  return this.then(
    value  => P.resolve(callback()).then(() => value),
    reason => P.resolve(callback()).then(() => { throw reason })
  );
};

从上述实现中可以看出,finally()方法返回原来的值(就是Promise抛出的值??)。

// resolve 的值是 undefined
Promise.resolve(2).then(() => {}, () => {})

// resolve 的值是 2
Promise.resolve(2).finally(() => {})

// reject 的值是 undefined
Promise.reject(3).then(() => {}, () => {})

// reject 的值是 3
Promise.reject(3).finally(() => {})

未完。。。

声明:本文内容由互联网用户自发贡献自行上传,本网站不拥有所有权,未作人工编辑处理,也不承担相关法律责任。如果您发现有涉嫌版权的内容,欢迎进行举报,并提供相关证据,工作人员会在5个工作日内联系你,一经查实,本站将立刻删除涉嫌侵权内容。