Promise

简介: Promise简洁


Callback 异步操作


es5中处理异步的时候,容易出现回调地狱的现象如


function loadScript (src, callback) {

 let script = document.createElement('script')

 script.src = src

 script.onload = () => callback(script)

 document.head.append(script)

}


loadScript('1.js', function (error, script) {

 if (error) {

   handleError(error)

 } else {

   // ...

   loadScript('2.js', function (error, script) {

     if (error) {

       handleError(error)

     } else {

       // ...

       loadScript('3.js', function (error, script) {

         if (error) {

           handleError(error)

         } else {

           // ...加载所有脚本后继续 (*)

         }

       })

     }

   })

 }

})


如果嵌套变多,代码层次就会变深,维护难度也随之增加,尤其是如果我们有一个不是 … 的真实代码,就会包含更多的循环,条件语句等。

这有时称为 “回调地狱” 或者“回调金字塔”。


Promise


将上面用callback来处理的问题转为用promise来处理


function loadScript (src) {

 return new Promise((resolve, reject) => {

   let script = document.createElement('script')

   script.src = src

   script.onload = () => resolve(script)

   script.onerror = (err) => reject(err)

   document.head.append(script)

 })

}


loadScript('1.js')

 .then(loadScript('2.js'), (err) => {

   console.log(err)

 })

 .then(loadScript('3.js'), (err) => {

   console.log(err)

 })


  • 基本语法


new Promise( function(resolve, reject) {…} );


通过创建 Promise 对象开启一个异步操作的过程,一般用几步完成多次异步操作:

  1. new Promise(fn) 返回一个Promise 对象
  2. 在fn 中指定异步等处理
  3. 处理结果正常的话,调用resolve(处理结果值)
  4. 处理结果错误的话,调用reject(Error对象)


  • 场景--封装一个ajax方法


function getURL (URL) {

 return new Promise(function (resolve, reject) {

   var req = new XMLHttpRequest()

   req.open('GET', URL, true)

   req.onload = function () {

     if (req.status === 200) {

       resolve(req.responseText)

     } else {

       reject(new Error(req.statusText))

     }

   }

   req.onerror = function () {

     reject(new Error(req.statusText))

   }

   req.send()

 })

}

// 运行示例

var URL = 'http://httpbin.org/get'

getURL(URL).then(function onFulfilled (value) {

 console.log(value)

}).catch(function onRejected (error) {

 console.error(error)

})



Promise 内部是有状态的(pending、fulfilled、rejected),Promise 对象根据状态来确定执行哪个方法。Promise 在实例化的时候状态是默认 pending 的,当异步操作是完成的,状态会被修改为 fulfilled,如果异步操作遇到异常,状态会被修改为 rejected,


状态转化是单向的,不可逆转,已经确定的状态(fulfilled/rejected)无法转回初始状态(pending),而且只能是从 pending 到 fulfilled 或者 rejected


then方法


.then()是一种链式调用的方法


.then是promise对象原型上的方法


  • 基本语法


promise.then(onFulfilled, onRejected);


例:


var promise = new Promise(function (resolve, reject) {

 resolve('传递给then的值')

})

promise.then(function (value) {

 console.log(value)

}, function (error) {

 console.error(error)

})


这段代码创建一个 Promise 对象,定义了处理 onFulfilled 和 onRejected 的函数(handler),然后返回这个 Promise 对象。


这个 Promise 对象会在变为 resolve 或者 reject 的时候分别调用相应注册的回调函数。


  • 当 handler 返回一个正常值的时候,这个值会传递给 Promise 对象的 onFulfilled 方法。
  • 定义的 handler 中产生异常的时候,这个值则会传递给 Promise 对象的 onRejected 方法。


回顾


function loadScript (src) {

 return new Promise((resolve, reject) => {

   let script = document.createElement('script')

   script.src = src

   script.onload = () => resolve(script)

   script.onerror = (err) => reject(err)

   document.head.append(script)

 })

}


loadScript('1.js')

 .then(loadScript('2.js'), (err) => {

   console.log(err)

 })

 .then(loadScript('3.js'), (err) => {

   console.log(err)

 })


回顾这段代码,因为promise(onFulfilled,onRejected),它的onFulfilled和onRejected是函数,当不是函数的时候,会被忽略,返回一个空的promise对象.

但是如上例子中,为什么被忽略掉还能执行呢?

因为其中的loadScript(2.js)它会被当成是一个表达式,来求取其值,求取结果的时候,它就会自然地被执行,从而返回一个promise对象.

既然应该是函数,那么该例应该写成以下写法,不然则会埋坑


loadScript('1.js')

 .then(()=> {

   return loadScript('2.js')

 }, (err) => {

   console.log(err)

 })

 .then(()=> {

   return loadScript('3.js')

 }, (err) => {

   console.log(err)

 })


极简promise雏形


function Promise(fn) {

   var value = null,

       callbacks = [];  //callbacks为数组,因为可能同时有很多个回调


   this.then = function (onFulfilled) {

       callbacks.push(onFulfilled);

   };


   function resolve(value) {

       callbacks.forEach(function (callback) {

           callback(value);

       });

   }


   fn(resolve);

}


大致逻辑是这样的:


  1. 调用then方法,将想要在Promise异步操作成功时执行的回调放入callbacks队列,其实也就是注册回调函数,可以向观察者模式方向思考;
  2. 创建Promise实例时传入的函数会被赋予一个函数类型的参数,即resolve,它接收一个参数value,代表异步操作返回的结果,当一步操作执行成功后,用户会调用resolve方法,这时候其实真正执行的操作是将callbacks队列中的回调一一执行;


Resolve & Reject 异步操作


一般情况下我们都会使用 new Promise() 来创建 Promise 对象,但是除此之外我们也可以使用其他方法。


静态方法 Promise.resolve(value) 可以认为是 new Promise() 方法的快捷方式。


比如 Promise.resolve(42) 可以认为是以下代码的语法糖。


new Promise(function (resolve) {

 resolve(42)

})


在这段代码中的 resolve(42) 会让这个 Promise 对象立即进入确定(即resolved)状态,并将 42 传递给后面 then 里所指定的 onFulfilled 函数。


方法 Promise.resolve(value) 的返回值也是一个 Promise 对象,所以我们可以像下面那样接着对其返回值进行 .then 调用。


Promise.resolve(42).then(function (value) {

 console.log(value)

})


Promise.reject(error) 是和 Promise.resolve(value) 类似的静态方法,是 new Promise() 方法的快捷方式。


比如 Promise.reject(new Error(“出错了”)) 就是下面代码的语法糖形式。


new Promise(function (resolve, reject) {

 reject(new Error('出错了'))

})


这段代码的功能是调用该Promise 对象通过then指定的 onRejected 函数,并将错误(Error)对象传递给这个 onRejected 函数。


Catch 异步操作


捕获异常是程序质量保障最基本的要求,可以使用 Promise 对象的 catch 方法来捕获异步操作过程中出现的任何异常。


  • 基本语法


p.catch(onRejected);


p.catch(function(reason) {

// rejection

});


以之前then的处理代码来看,对它进行修改,使用catch来处理出错


function loadScript (src) {

 return new Promise((resolve, reject) => {

   let script = document.createElement('script')

   script.src = src

   script.onload = () => resolve(script)

   script.onerror = (err) => reject(err)

   document.head.append(script)

 })

}


loadScript('1.js')

 .then(()=> {

   return loadScript('2.js')

 })

 .then(()=> {

   return loadScript('3.js')

 })

 .catch(err => {

   console.log(err)

 })


建议用 reject(new Error())的方式来触发错误,而不是使用throw new Error来触发

因为 throw 的方式并没有改变 Pronise 的状态


All 异步操作


  • 基本语法


Promise.all(promiseArray);


Promise.all 生成并返回一个新的 Promise 对象,所以它可以使用 Promise 实例的所有方法。参数传递promise数组中所有的 Promise 对象都变为resolve的时候,该方法才会返回, 新创建的 Promise 则会使用这些 promise 的值。


如果参数中的任何一个promise为reject的话,则整个Promise.all调用会立即终止,并返回一个reject的新的 Promise 对象。


由于参数数组中的每个元素都是由 Promise.resolve 包装(wrap)的,所以Promise.all 可以处理不同类型的 promose对象。


示例:


var p1 = Promise.resolve(1)

var p2 = Promise.resolve(2)

var p3 = Promise.resolve(3)

Promise.all([p1, p2, p3]).then((results) => {

 console.log(results) // [1, 2, 3]

})


Race 异步操作


Promise.race 生成并返回一个新的 Promise 对象.

参数 promise 数组中的任何一个 Promise 对象如果变为 resolve 或者 reject 的话, 该函数就会返回,并使用这个 Promise 对象的值进行 resolve 或者 reject。


例:


var p1 = Promise.resolve(1)

var p2 = Promise.resolve(2)

var p3 = Promise.resolve(3)

Promise.race([p1, p2, p3]).then((value) => {

 console.log(value) // 1

})


参考资料



更多


目录
相关文章
|
6月前
|
前端开发 API 容器
说说你对 promise 的了解
说说你对 promise 的了解
33 0
|
前端开发 小程序 JavaScript
promise 应用
promise 应用
58 0
|
3月前
|
前端开发 JavaScript
Promise相关的理解
总之,Promise 是现代 JavaScript 异步编程的基石。它们提供了一种优雅的方式来处理异步操作,易于读写和维护,还可以通过其方法来控制复杂的异步流程。
44 5
|
5月前
|
前端开发
|
6月前
|
前端开发
对Promise的理解
对Promise的理解
41 2
|
存储 JSON 前端开发
深入使用 Promise
前面各种铺垫已经好了,现在我们一起来用一用Promise!
72 0
|
前端开发
对promise的理解分享
对promise的理解分享
|
前端开发 JavaScript
深入理解Promise
深入理解Promise
88 0
|
前端开发 数据库
promise的介绍
promise的介绍
104 0
|
前端开发 JavaScript
探索Promise的应用
探索Promise的应用
118 0