Skip to content

Latest commit

 

History

History
282 lines (236 loc) · 7.34 KB

Promise.md

File metadata and controls

282 lines (236 loc) · 7.34 KB

预览

function asyncFunc(){
  return new Promise(
    function(resolve,reject){
      let value,error;
      resolve(value); // success
      // ...
      reject(error); // failure
    }
  )
}
asyncFunc()
.then(value => { /*success*/ })
.catch(error => { /*failure*/ })

含义

Promise是一部编程的一种解决方案————回调函数和事件————更合理且更强大。
Promise对象是一个构造函数,用来生产Promise实例。
Promise提供统一的API,各种一部操作都可以用同样的方法进行处理。

场景:

回调地狱:回调嵌套过多,代码繁琐。

特点:

  • 本质还是回调函数。
  • 区分成功失败的回调,省去嵌套在内层的嵌套逻辑。
  • 可以很轻松的完成回调函数模式到Promise模式的转化。
  • 代码由回调函数嵌套的横向扩展,变为链式调用的纵向扩展,易于理解和维护。
  • 对象的状态不受外界的影响。Promise三种状态:Pending(进行中)、Fulfilled(已成功)、Rejected(已失败)。
  • 一旦状态改变就不会再变。Promise状态改变的两种可能:从Pending变为Fulfilled、从Pending变为Rejected

注意:无法取消Promise,一旦新建它就会立即执行,无法中途取消;如果不设置回调函数,Promise内部抛出的错误不会反应到外部;当处于Pending状态时,无法得知目前进展到哪一阶段(刚刚开始还是即将完成)。

Promise构造函数接受一个参数:函数,并且这个函数需要传入两个参数:

resolve函数: 将Promise对象的状态从未完成变为成功(即从Pending变为Resolved),在异步成功时调用,并将异步成功的结果作为参数传递出去;
reject函数:将Promise对象的状态从未完成变为失败(即从Pending变为Rejected),在异步操作失败时调用,并将异步操作失败报出的错误作为参数传递出去。

基本用法

就比如如下式的执行顺序,依次打印出的结果:

function timeout(ms){
  return new  Promise((resolve, reject)=>{
    setTimeout(resolve, ms, 'done')
  })
}
timeout(100).then(value=>{
  console.log(value);
})

// Promise新建后就会立即执行
let promise = new Promise(function(resolve, reject){
  console.log('Promise1');
  resolve();
  console.log('Promise2');
})
promise.then(function(){
  console.log('Resolved.');
})
console.log('hi');
// Promise
// hi 
// Resolved
// done

从上式中不难看出,Promise新建后就会立即执行;Promise构造函数是同步的,promise.then中的函数是一步执行的。

Promise.prototype.then()

接受两个参数:1.第一个参数是Resolved状态的回调函数;2.第二个参数(可选)是Rejected状态的回调函数。
一般来说,不要在then方法中定义Rejected状态的回调函数(即then的第二个参数),而应总是使用catch方法。

Promise.prototype.catch()

和then的第二个参数一样,用来指定Rejected的回调。
在执行Resolved状态的回调函数时,如果抛出异常了(代码出错了),那么并不会报错卡死js,而是会进到catch方法中。

let p = new Promise(function(resolve, reject){
  resolve(111);
})
p.then(data=>{
  console.log('resolved',data); // resolved 111
  console.log(sData); // 
}).catch(err=>{
  console.log('rejected',err); // rejected ReferenceError: sData is not defined
})

Promise.all()

多个Promise实例包装成一个新的Promise实例。

let p = Promise.all([p1,p2,p3]);

只有p1,p2,p3的状态都变成Fulfilled,p的状态才会变成Fulfilled,此时p1,p2,p3的返回值组成一个数组,传递给p的回调函数。
只要p1,p2,p3中有一个被Rejected,p的状态就变成Rejected,此时第一个被Rejected的实例的返回值会传递给P的回调函数。

Promise.race()

多个Promise实例包装成一个新的实例。

let p = Promise.race([p1,p2,p3]);

只要p1,p2,p3中有一个实例率先改变状态,p的状态就跟着改变。那个率先改变的Promise实例的返回值就传递给p的回调函数。

Promise.resolve()

将现有对象转为Promise对象。

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

Promise.reject()

也会返回一个新的Promise实例,状态为Rejected。

const p = Promise.reject('出错了');
// 等同于
const p1 = new Promise((resolve,reject)=>reject('出错了'));
p1.then(null,function(s){
  console.log(s);
})

done()

总是处于回调链的尾端,保证抛出任何可能出现的错误。

用法:

asyncFunc()
.then(f1)
.catch(r1)
.then(f2)
.done()

实现代码:

Promise.prototype.done=function(onFulfilled,onRejected){
  this.then(onFulfilled,onRejected)
  .catch(function(reason){
    // 抛出一个全局错误
    setTimeout(()=>{throw reson},0)
  })
}

finally()

用于指定不管Promise对象最后状态如何都会执行的操作。

用法:

server.listen(0)
.then(function(){
  // run test
})
.finally(server.stop)

实现:

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})
  )
}

应用

  • 加载图片
const preloadImage = function(path){
  return new Promise(function(resolve,reject){
    const image = new Image();
    image.onload = resolve;
    image.onerror = reject;
    image.src = path
  })
}
  • Promise化fs.readFile()
import {readFile} from 'fs';

function readFilePromisified(filename){
  return new Promise(
    function(resolve, reject){
      readFile(filename,{encoding:'utf8'},
        (error,data)=>{
          if(error){
            reject(error);
          }
          resolve(data)
        }
      )
    }
  )
}

readFilePromisified(process.argv[2])
.then(text=>{
  console.log(text);
})
.catch(error=>{
  console.log(error);
})
  • Promise化XMLHttpRequest
function httpGet(url){
  return new Promise(function(resolve,reject){
    let request = new XMLHttpRequest();
    request.onreadystatechange = function(){
      if(this.status === 200){
        //success
        resolve(this.response);
      }else{
        reject(new Error(this.statusText))
      }
    }
    request.onerror = function(){
      reject(new Error(`XMLHttpRequest Error:${this.statusText}`))
    }
    request.open('GET', url);
    request.send();
  })
}

httpGet('http://example.com/file.text')
.then(
  function(value){
    console.log(value);
  },
  function(reason){
    console.error('error:'+reason);
  }
)
  • 延迟操作
function delay(ms){
  return new Promise(function(resolve, reject){
    setTimeout(resolve,ms);
  })
}
delay(5000).then(function(){
  console.log('5 seconds have passed!');
})
  • Promise超时
function timeout(ms,promise){
  return new Promise(function(resolve,reject){
    promise.then(resolve);
    setTimeout(function(){
      reject(new Error(`Timeout after ${ms} ms`));
    },ms)
  })
}