前端修仙之路

ES6-Promise从入门到实战(AJax和fetch)

2019-07-12  本文已影响2人  月上秦少

Promise定义

MDN Promise

A Promise is an object that is used as a placeholder for the eventual results of a deferred (and possibly asynchronous) computation.

Promise是一个对象,用作占位符,用于延迟(可能是异步)计算的最终结果。

一个 Promise 就是一个对象,它代表了一个异步操作的最终完成或者失败。

本质上,Promise 是一个绑定了回调的对象,而不是将回调传进函数内部。

Promise State

Promise只会处在以下状态之一:

  1. Pending(待处理): promise初始化的状态,正在运行,既没有完成也没有失败的状态,此状态可以提升为fulfilledrejected状态

  2. Fulfilled(已完成): 如果回调函数实现Promise的resolve(回调函数),那么state变为fulfilled

  3. Rejected(已拒绝): 如果Promise调用过程中遭到拒绝或发生异常,state就会处于rejected状态。

  4. Settled(不变的): Promise从pending状态提升后,状态要么变为fulfilled,要么变为rejected,没有其他情况。

即:

Promise 状态转移:
1. pending ---> fulfilled ---> settled
2. pending ---> rejected ---> settled
Promise 状态转移

情景模式

一次苏洵在家宴客,限以冷、香二字为联,并先出一联为“水向石边流出冷,风从花里过来香。”苏轼当场吟出一联:“拂石坐来衣带冷,踏花归去马蹄香。”

对对子的套路很简单,就是一人出上联,一人对下联。

function up() {
    console.log('水向石边流出冷,风从花里过来香。');
    return '水向石边流出冷,风从花里过来香。';
}

function down() {
    console.log('拂石坐来衣带冷,踏花归去马蹄香。');
    return '拂石坐来衣带冷,踏花归去马蹄香。';
}

function match () {
    // 给1s钟出上联
    setTimeout(up, 1000);
    // 给2s钟对下联
    setTimeout(down, 1000);
}
match(); // 水向石边流出冷,风从花里过来香。
         //拂石坐来衣带冷,踏花归去马蹄香。

上面的例子很简单,down函数总是需要在up函数之后执行。

如果我们用Promise来实现如下。

const match = new Promise((resolve, reject) => {
    // 苏洵说
    console.log('苏洵:\n游戏规则:限以冷、香二字为联');
     const time = 1000;
    setTimeout(() => {
        console.log(`苏洵:\n我出上联,且容我思考${time / 1000}s钟!`);
        resolve(time);
    }, time);
}).then(value => {
    console.log(`苏洵:\n经过${value / 1000}s钟思考,我的上联是:`);
    console.log('水向石边流出冷,风从花里过来香。');
    return value * 2;
}).then((value) => {
    console.log('宾客:\n这上联出的有水平呀!');
    console.log(`宾客:且容我思考${value / 1000}s钟!`);
    return value / 4;
}).then(value => {
        console.log(`苏轼:经过${value / 1000}s钟思考,我的下联是:`);
        console.log('拂石坐来衣带冷,踏花归去马蹄香。');
        console.log(`宾客:。。。`);
}).then(() => {
    console.log('犬子对的妙啊,不愧我儿!nice!')
});

上述例子输出结果:

苏洵:
游戏规则:限以冷、香二字为联
undefined
苏洵:
我出上联,且容我思考1s钟!
苏洵:
经过1s钟思考,我的上联是:
水向石边流出冷,风从花里过来香。
宾客:
这上联出的有水平呀!
宾客:且容我思考2s钟!
苏轼:经过0.5s钟思考,我的下联是:
拂石坐来衣带冷,踏花归去马蹄香。
宾客:。。。
犬子对的妙啊,不愧我儿!nice!

不懂,没关系!啥,你说你不懂对对子,没关系, 下面我们来具体讲讲Promise的API。

1.Promise实现——(resolve, reject) 方法

如:foo()和bar()函数都实现promise

function foo() {return new Promise((resolve, reject) => {
    resolve();
})}

function bar() {return new Promise((resolve, reject) => {
    resolve();
})}

// 使用
foo() 
.then( res => bar() ) // bar() returns a Promise 
.then( res => { 
 console.log('Both done') 
})

2.Promise.then(onFulfilled, onRejected) 方法

Promise的then()方法允许我们在任务完成后或拒绝失败后执行任务,该任务可以是基于另外一个事件或基于回调的异步操作。

Promise的then()方法接收两个参数,即onFulfilledonRejected 的回调,如果Promise对象完成,则执行onFulfilled回调,如果执行异常或失败执行onRejected回调。

简单的来说,onFulfilled回调接收一个参数,及所谓的未来的值,同样 onRejected 也接收一个参数,显示拒绝的原因。
(ajaxCallPromise后面有实现这个方法)

ajaxCallPromise('http://example.com/page1').then( 
 successData => { console.log('Request was successful') }, 
 failData => { console.log('Request failed' + failData) } 
)

如果请求过程失败,第二个函数将会执行输出而不是第一个函数输出。

3.Promise.catch(onRejected)方法

除了上述then()方法可以处理错误和异常,使用Promise的catch()方法也能实现同样的功能,这个方法其实并没有什么特别,只是更容易理解而已。

catch()方法只接收一个参数,及onRejected回调。catch()方法的onRejected回调的调用方式与then()方法的onRejected回调相同。

ajaxCallPromise('http://example.com/page1')
.then(successData => console.log('Request was successful'))
.catch(failData => console.log('Request failed' + failData));

4.Promise.resolve(value)方法

Promise的resolve()方法接收成功返回值并返回一个Promise对象,用于未来值的传递,将值传递给.then(onFulfilled, onRejected)onFulfilled回调中。resolve()方法可以用于将未来值转化成Promise对象,下面的一段代码演示了如何使用Promise.resolve()方法:

const p1 = Promise.resolve(4); 
p1.then(function(value){ 
 console.log(value); 
}); //passed a promise object 
Promise.resolve(p1).then(function(value){ 
 console.log(value); 
}); 
Promise.resolve({name: "Eden"}) 
.then(function(value){ 
 console.log(value.name); 
});

// 输出
//  4 
//  4 
//  Eden

5.Promise.reject(value)方法

Promise.reject(value)方法与上述Promise.resolve(value)类似,唯一不同的是将值传递给.then(onFulfilled, onRejected)onRejected回调中,同时Promise.reject(value)主要用来进行调试,而不是将值转换成Promise对象。

const p1 = Promise.reject(4); 
p1.then(null, function(value){ 
console.log(value); 
}); 
Promise.reject({name: "Eden"}) 
.then(null, function(value){ 
 console.log(value.name); 
});
// 4 
// Eden

6.Promise.all(iterable) 方法

Promise.all(iterable) 方法可以传入一个可迭代Promise对象(比如数组)并返回一个Promise对象,当所有的Promise迭代对象成功返回后,整个Promise才能返回成功状态的值。

const p1 = new Promise(function(resolve, reject){ 
     setTimeout(function(){ 
     resolve(); 
     }, 1000); 
}); 

const p2 = new Promise(function(resolve, reject){ 
    setTimeout(function(){ 
     resolve(); 
     }, 2000); 
}); 

// 使用
const arr = [p1, p2]; 
Promise.all(arr).then(function(){ 
    console.log("Done"); //"Done" is logged after 2 seconds 
});

特别需要注意的一点,只要迭代数组中,只要任意一个进入失败状态,那么该方法返回的对象也会进入失败状态,并将那个进入失败状态的错误信息作为自己的错误信息。

const p1 = new Promise(function(resolve, reject){ 
     setTimeout(function(){ 
     reject("Error"); // 返回错误信息
     }, 1000); 
}); 

const p2 = new Promise(function(resolve, reject){ 
     setTimeout(function(){ 
     resolve(); 
     }, 2000); 
}); 

const arr = [p1, p2]; 
Promise.all(arr).then(null, function(reason){ 
    console.log(reason); //"Error" is logged after 1 second 
});

7.Promise.race(iterable) 方法

Promise.all(iterable) 不同的是,Promise.race(iterable) 虽然也接收包含若干个Promise对象的可迭代对象,不同的是这个方法会监听所有的Promise对象,并等待其中的第一个进入完成或失败状态的Promise对象,一旦有Promise对象满足,整个Promise对象将返回这个Promise对象的成功状态或失败状态,下面的示例展示了返回第一个成功状态的值:

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

var p2 = new Promise(function(resolve, reject){ 
    setTimeout(function(){ 
    resolve("fulfillment Value 2"); 
    }, 2000); 
}); 

var arr = [p1, p2]; 
Promise.race(arr).then(function(value){ 
    console.log(value); //Output "Fulfillment value 1" 
    }, function(reason){ 
    console.log(reason);
});

接下来我们来使用Promise。

Ajax使用Promise

先实现一个原生的Ajax:

const Ajax = {
  get: function(url, fn) {
    // XMLHttpRequest对象用于在后台与服务器交换数据   
    const xhr = new XMLHttpRequest();            
    xhr.open('GET', url, true);
    xhr.onreadystatechange = function() {
      // readyState == 4说明请求已完成
      if (xhr.readyState === 4 && xhr.status === 200 || xhr.status === 304) { 
        // 从服务器获得数据 
        fn.call(this, xhr.responseText);  
      }
    };
    xhr.send();
  },
  // datat应为'a=a1&b=b1'这种字符串格式,在jq里如果data为对象会自动将对象转成这种字符串格式
  post: function (url, data, fn) {
    const xhr = new XMLHttpRequest();
    xhr.open("POST", url, true);
    // 添加http头,发送信息至服务器时内容编码类型
    xhr.setRequestHeader("Content-Type", "application/x-www-form-urlencoded");  
    xhr.onreadystatechange = function() {
      if (xhr.readyState === 4 && (xhr.status === 200 || xhr.status === 304)) {
        fn.call(this, xhr.responseText);
      }
    };
    xhr.send(data);
  }
}

下面使用Ajax请求数据:


const ajaxCallPromise = url => {
    return new Promise((resolve, reject) => {
        // DO YOUR ASYNC STUFF HERE 
        Ajax.get(url, function(data) {
            if(data.resCode === 200) {
                resolve(data.message)
            } else {
                reject(data.error)
            } 
        })
    }) 
};

// 使用:
ajaxCallPromise('http://example.com/page1') 
.then( response1 => ajaxCallPromise('http://example.com/page2'+response1) ) 
.then( response2 => ajaxCallPromise('http://example.com/page3'+response2) ) 
.then( response3 => console.log(response3) )
  1. 首先定义ajaxCallPromise返回类型为Promise,这意味我们会实现一个Promise对象。
  2. Promise接受两个函数参数,resolve(成功实现承诺)和reject(异常导致失败)
  3. resolve和reject这两个特有的方法,会获取对应成功或失败的值
  4. 如果接口请求一切正常,我们将会接收到resolve函数返回的值
  5. 如果接口请求失败,我们将会接收到reject返回的的值

fetch默认使用Promise

fetch返回一个包含响应结果的promise(一个 Response 对象)对象。

fetch('http://example.com/movies.json')
  // 现将请求转为json
  .then(function(response) {
    return response.json();
  })
  // 在控制台打印出json
  .then(function(myJson) {
    console.log(myJson);
  });

// 上传图片,处理错误
fetch('flowers.jpg').then(function(response) {
  if(response.ok) {
    return response.blob();
  }
  throw new Error('Network response was not ok.');
}).then(function(myBlob) { 
  var objectURL = URL.createObjectURL(myBlob); 
  myImage.src = objectURL; 
}).catch(function(error) {
  console.log('There has been a problem with your fetch operation: ', error.message);
});

MDN fetch

fetch优势:

fetch的确很好用,但是有的浏览器确是undefined(比如万恶的IE),这时就需要我们手动实现ajax作为替代,或者引入polyfill。

fetch原生支持率不高,但是引入下面这些 polyfill 后可以完美支持 IE8+:

参考:
Promise使用指南
MDN Promise

上一篇下一篇

猜你喜欢

热点阅读