promise用法解析

1.概述

javascript是單線程語言(單線程/多線程、阻塞/非阻塞、同步、異步)參考此文章,全部的任務都是按順序執行的,可是對於很耗時的操做採用異步的方式(前一個任務結束的時候,不是執行下一個任務,而是執行回調函數,後一個任務則是不等前一個任務結束就執行)參考此文章,js中異步編程的四種方法:回調函數、事件監聽、發佈/訂閱、Promise對象,這裏討論promise的用法,promise是es6增長的內容,使得在異步編程中不用過多的嵌套回調函數,讓異步操做變得更加簡單javascript

2.Promise介紹

2.1 Promise構造函數

Promise構造函數接收一個函數做爲參數,此函數又有兩個參數分別爲resolve和reject,這兩個參數也是函數php

const promise = new Promise(function(resolve, reject) {
  // 異步操做的代碼
  if(success) {
    return resolve(data); // data爲異步操做成功返回的數據
  } else {
    return reject(error); //data爲失敗時返回的數據
  }
})

複製代碼

2.2 resolve和reject

promise對象相似於一個容器(也能夠說是一個狀態機),裏面包含着異步操做,異步操做會有兩種結果:成功或失敗。當異步操做成功就會將pending(執行中狀態)轉爲fulfilled(成功狀態)同時觸發resolve函數,用來將操做成功後獲得的結果傳遞出去;當異步操做失敗就會將pending(執行中狀態)轉爲reject(拒絕狀態)同時觸發reject函數,用來將操做失敗後報出的錯誤傳遞出去java

const promise = new Promise(function(resolve, reject) {
  // 異步操做的代碼
  if(success) {
    return resolve(data); // data爲異步操做成功返回的數據
  } else {
    return reject(error); //data爲失敗時返回的數據
  }
})
複製代碼

咱們來看下面代碼:ios

function fn1() {
    return new Promise(function(resolve, reject){
      setTimeout(() => {
        console.log('fn1')
      },1000)
    })
  }
  function fn2() {
    return new Promise(function(resolve, reject){
      setTimeout(() => {
        console.log('fn2')
      },2000)
    })
  }
fn1().then(fn2)
複製代碼

輸出爲:
fn1
fn2函數不執行,這個時候咱們須要調用resolve函數以便執行then()方法中的回調函數fn2es6

function fn1() {
    return new Promise(function(resolve, reject){
      setTimeout(() => {
        console.log('fn1')
        resolve('fn1')
      },1000)
    })
  }
複製代碼

輸出爲:
fn1
fn2
若是咱們在fn1中調用reject函數時會出現什麼狀況呢?ajax

function fn1() {
    return new Promise(function(resolve, reject){
      setTimeout(() => {
        console.log('fn1')
        // resolve('fn1')
        reject('錯誤'+'fn1')
      },1000)
    })
  }
  fn1().then(fn2).then((data) => {
    console.log(data)
  })
複製代碼

輸出爲:編程


提示錯誤未捕獲,因此須要在then()方法中添加第二個回到函數來處理出錯信息

fn1().then(fn2).then((data) => {
    console.log(data)
  }, (err) => {
    console.log(err)
  })
複製代碼

輸出爲:json

2.3 then()方法

當promise實例生成之後,後面跟then方法,其的第一個回調函數來處理resolve函數傳遞的數據,第二個回調函數來處理reject函數傳遞的數據,以上的流程用代碼展現以下axios

promise
  .then(function(data){
    //拿到返回的數據以後作一些處理
    console.log(data)
  }, function(error) {
    //返回失敗時作一些處理
    console.log(error)
  })
複製代碼

2.3.1 then()的返回值

then()方式是Promise實例的方法,此then方法定義在原型對象(Promise.prototype)上,then()方法的返回值是一個新的Promise實例(不是原來那個Promise,原來那個Promise已經承諾過)因此咱們能夠採用鏈式的寫法segmentfault

var p1 = new Promise( (resolve, reject) => {
    setTimeout(() => resolve('p1'), 10);
});

p1.then( ret => {
    console.log(ret);
    return 'then1';
}).then( ret => {
    console.log(ret);
    return 'then2';
}).then( ret => {
    console.log(ret);
});
複製代碼

執行順序:
p1>then1>then2
從第二個then()方法開始,它們的resolve中的參數就是前一個then()中的resolve的return語句的返回值 採用鏈式的then,能夠指定一組按照次序調用的回調函數。這時,前一個回調函數,有可能返回的仍是一個Promise對象(即有異步操做),這時後一個回調函數,就會等待該Promise對象的狀態發生變化,纔會被調用

getJSON("/post/1.json").then(function(post) {
  return getJSON(post.commentURL);}).then(function funcA(comments) {
  console.log("resolved: ", comments);}, function funcB(err){
  console.log("rejected: ", err);}) 
複製代碼

上面代碼中,第一個then方法指定的回調函數,返回的是另外一個Promise對象。這時,第二個then方法指定的回調函數,就會等待這個新的Promise對象狀態發生變化。若是變爲resolved,就調用funcA,若是狀態變爲rejected,就調用funcB。 若是採用箭頭函數,上面的代碼能夠寫得更簡潔

getJSON("/post/1.json").then(
  post => getJSON(post.commentURL)).then(
  comments => console.log("resolved: ", comments),
  err => console.log("rejected: ", err));
複製代碼

promise鏈式寫法以下:

function fn1() {
    return new Promise(function(resolve, reject){
      setTimeout(() => {
        console.log('fn1')
        resolve('fn1')
      },1000)
    })
  }
  function fn2() {
    return new Promise(function(resolve, reject){
      setTimeout(() => {
        console.log('fn2')
        resolve('fn2')
      },2000)
    })
  }
  function fn3() {
    return new Promise(function(resolve, reject){
      setTimeout(() => {
        console.log('fn3')
        resolve('fn3')
      },3000)
    })
  }
  function fn4() {
    return new Promise(function(resolve, reject){
      setTimeout(() => {
        console.log('fn4')
        resolve('fn4')
      },4000)
    })
  }
  fn1().then(fn2).then(fn3).then((data) => {
    console.log(data)
  }, (err) => {
    console.log(err)
  })
複製代碼

依次輸出爲:
fn1>fn2>fn3
同時咱們還能夠更改執行的前後順序

function fn1() {
    return new Promise(function(resolve, reject){
      setTimeout(() => {
        console.log('fn1')
        reject(false)
      },1000)
    })
  }
  function fn2() {
    return new Promise(function(resolve, reject){
      setTimeout(() => {
        console.log('fn2')
        resolve('fn2')
      },2000)
    })
  }
  function fn3() {
    return new Promise(function(resolve, reject){
      setTimeout(() => {
        console.log('fn3')
        resolve('fn3')
      },3000)
    })
  }
  function fn4() {
    return new Promise(function(resolve, reject){
      setTimeout(() => {
        console.log('fn4')
        resolve('fn4')
      },4000)
    })
  }
  fn1().then(fn2).then(fn3).then((data) => {
    console.log(data)
  }, (err) => {
    if(err == false){
      fn3().then(fn4)
    }
  })
複製代碼

輸出爲:
fn1>fn3>fn4

2.4 catch()方法

咱們在開發時傾向於用catch()方法來處理異常,而不是在then()方法裏寫入第二個回調函數,這種寫法相似於.then(null, rejection)

promise
  .then(function(data){
    //拿到返回的數據以後作一些處理
    console.log(data)
  })
  .catch(function(error) {
    //返回失敗時作一些處理
    console.log(error)
  }

複製代碼

爲何要採用這麼catch()方法呢?咱們來看一個例子:

const promise = new Promise((resolve,reject) => {
  console.log(1)
  resolve('成功')
})
promise
  .then((data) => {
    console.log(data)
    console.log(a)
  }, (err) => {
    console.log(err)
  })

複製代碼

輸出爲:
1
成功
可是沒有捕捉到回調函數裏a未定義 這個時候咱們來該寫以上代碼
1
成功
ReferenceError: a is not defined
Promise對象的Error對象具備冒泡性質,會一直向後傳遞,直到被捕獲爲止。也就是說,錯誤老是會被下一個catch語句捕獲

var p = new Promise( (resolve, reject) => {
    setTimeout(() => resolve('p1'), 10);
});

p.then( ret => {
    console.log(ret);
    throw new Error('then1');
    return 'then1';
}).then( ret => {
    console.log(ret);
    throw new Error('then2');
    return 'then2';
}).catch( err => {
    // 能夠捕抓到前面的出現的錯誤。
    console.log(err.toString());
});

複製代碼

輸出爲:
p1
Error: then1

2.4.1 catch()返回值

既然catch()是.then(null, rejection)的別名,那麼catch()就會返回一個Promise對象,所以在後面還能夠接着調用then方法

var p = new Promise((resolve, reject) => {
    resolve(x + 2);
});
p.then( () => {
    console.log('nothing');
}).catch( err => {
    console.log(err.toString());
    return 'catch';
}).then( ret => {
    console.log(ret);
});

複製代碼

輸出爲:
ReferenceError: x is not defined
catch
當出錯時,catch會先處理以前的錯誤,而後經過return語句,將值繼續傳遞給後一個then方法中,若是沒有報錯,則跳過catch,示例以下:

var p = new Promise((resolve, reject) => {
    resolve('p');
});
p.then( ret => {
    console.log(ret);
    return 'then1';
}).catch( err => {
    console.log(err.toString());
    return 'catch';
}).then( ret => {
    console.log(ret);
});

複製代碼

3. promise用法解析

3.1 用Promise實現ajax操做

const getJSON = function(url) {
  const promise = new Promise(function(resolve, reject){
    const handler = function() {
      if (this.readyState !== 4) {
        return;
      }
      if (this.status === 200) {
        resolve(this.response);
      } else {
        reject(new Error(this.statusText));
      }
    };
    const client = new XMLHttpRequest();
    client.open("GET", url);
    client.onreadystatechange = handler;
    client.responseType = "json";
    client.setRequestHeader("Accept", "application/json");
    client.send();

  });

  return promise;
};

getJSON("/posts.json").then(function(json) {
  console.log('Contents: ' + json);
}, function(error) {
  console.error('出錯了', error);
});

複製代碼

3.2 promise和ajax方法結合

var http = {
    get: function(url) {
        var promise = new Promise(function(resolve, reject) {
            $.ajax({
                url: url,
                method: 'get',
                success: function(data) {
                    resolve(data);
                },
                error: function(xhr, statusText) {
                    reject(statusText);
                }
            });
        });
        return promise;
    }
};
http.get('data.php').then(function(data) {
    document.write(data);
}, function(err) {
    document.write(err);
});

複製代碼

3.3 用promise實現異步加載圖片的例子

function loadImageAsync(url) {
  return new Promise(function(resolve, reject) {
    const image = new Image();

    image.onload = function() {
      resolve(image);
    };

    image.onerror = function() {
      reject(new Error('Could not load image at ' + url));
    };

    image.src = url;
  });
}
var loadImage1 = loadImageAsync(url);
loadImage1.then(function success() {
    console.log("success");
}, function fail() {
    console.log("fail");
});

複製代碼

3.4 promise和axios方法結合

function fetch(url, params) {
  return new Promise((resolve, reject) => {
    axios.post(url, params)
      .then(response => {
        resolve(response.data);
      }, error => {
        reject(error);
      })
      .catch(error => {
        reject(error)
      })
  })
}

function lineStatus(params) {
  return fetch('/ProductionLine/GetStatus', params)
}
function statisticsData(params) {
  return fetch('/ProductionLine/GetWeightStatistics', params)
}

      lineStatus(this.lineId)
        .then((result) => {
          this.deviceStatus.run = result.TotleMoveMotor
          this.deviceStatus.stop = result.TotleStopMotor
          this.deviceStatus.lost = result.TotleLoseMotor
          this.deviceStatus.alarm = result.TotleAlarmMotor
          this.ProductionStatus = result.ProductionStatus
          console.log(result)
        })
        .catch((error) => {
          console.log('瓦特了...(;′⌒`)')
        })

      statisticsData(this.lineId)
        .then((result) => {
          this.outputData.totalOutput = result.MainConveyorModel.OutPut
          this.outputData.instantWeight = result.MainConveyorModel.InstantWeight
          this.outputData.runningTime = result.MainConveyorModel.RunningTime
          this.outputData.motorLoad = result.MainConveyorModel.MotorLoad
          // console.log(result)
        })
        .catch((error) => {
          console.log('瓦特了...(;′⌒`)')
        })

複製代碼

以上是Promise在開發中常見的用法,參考瞭如下幾篇文章,特此感謝做者

相關文章
相關標籤/搜索