Promise原理講解 async+await應用(非同步回調解決方案)

來源:互聯網
上載者:User

標籤:兩種   cte   參考   方法參數   value   觀察   返回   點擊   executor   

  • 1.非同步編程
    • 1.1.回呼函數
    • 1.2 發布訂閱
    • 1.3 觀察者模式
    • 1.4 Promise
  • 2.promise用法與原理
    • 2.1 Promise.prototype.then()
    • 2.2 Promise.prototype.catch()
    • 2.3 Promise.all
    • 2.4 Promise.race
    • 2.5 Promise.resolve
    • 2.6 Promise.reject
    • 2.7 promise的一些擴充庫
    • 2.8 應用 async + await = generator + co
  • 3.手寫一個promise A+
  • 參考文檔
1.非同步編程

JavaScript的世界中,所有代碼都是單線執行的。 由於這個“缺陷”,導致JavaScript的所有網路操作,瀏覽器事件,都必須是非同步執行。非同步執行可以用:

  • 回呼函數
  • 發布訂閱
  • 觀察者模式
  • promise
1.1.回呼函數
function call(id, callback){  return function(){     callback(id+1);  }}let fn = call(3, function(id){  console.log(id);})fn();

lodash 裡面的after函數實現方法

function after(times, callback){  return function(){    //次數一直減少    if(--times == 0){      callback();    }  }}let fn = after(3, function(){  console.log(‘after 被調用了三次‘);})fn();fn();fn();

接下來就是常見的讀取資料的問題,回呼函數的話,我們只能一層一層往下讀取,很容易就進入了回調地獄這個可怕的狀態

let fs = require(‘fs‘);let school = {}fs.readFile(‘./age.txt‘, ‘utf8‘, function (err, data) {    school[‘name‘] = data;    fs.readFile(‘./name.txt‘, ‘utf8‘, function (err, data) {      school[‘age‘] = data;//{ name: ‘cjw‘, age: ‘18‘ }    });});
1.2 發布訂閱

發行者和訂閱者是沒有依賴關係的

你可能對發布訂閱有點陌生,其實只要在DOM節點上面綁定過事件函數,那就使用過發布—訂閱模式。

document.body.addEventListener(‘click‘,function(){  alert(2);},false);document.body.click();    //類比使用者點擊

實現原理

首先用一個數組arr儲存回呼函數,然後觸發emit的時候,arr裡面的回呼函數一一執行

let fs = require(‘fs‘);let dep = {    arr: [],//儲存回呼函數    on(callback){        this.arr.push(callback);    },    emit(){        this.arr.forEach(item=>{            item();        })    }}let school = {};//這裡先加一個回呼函數 (訂閱)dep.on(function(){    if(Object.keys(school).length === 2){        console.log(school);//{ name: ‘cjw‘, age: ‘18‘ }    }})//fs.readFile(‘./age.txt‘, ‘utf8‘, function(err, data){    school[‘name‘] = data;    dep.emit();//發布,調用dep.arr 裡面的回呼函數一一執行})fs.readFile(‘./name.txt‘, ‘utf8‘, function(err, data){    school[‘age‘] = data;    dep.emit();//發布})
1.3 觀察者模式

觀察者模式 發布和訂閱的 被觀察者是依賴於觀察者的

//觀察者class Observer{    constructor(){        this.arr = [];        this.val = 1;    }    updateVal(val){        this.val = val;        this.notify();    }    notify(){        this.arr.forEach(s=>s.update());    }    save(s){//儲存一個對象        this.arr.push(s);    }}// 被觀察者,被觀察者有一個更新的方法。class Subject{    update(){        console.log(‘update‘)    }}let s = new Subject();let observer = new Observer();observer.save(s);//儲存一個對象observer.save(s);observer.updateVal(21);//更新值的時候,被觀察者也執行一個更新的方法
1.4 Promise

promise有以下兩個特點:

1.對象的狀態不受外界影響。Promise對象代表一個非同步作業,有三種狀態:pending(進行中)、fulfilled(已成功)和rejected(已失敗)。只有非同步作業的結果,可以決定當前是哪一種狀態,任何其他動作都無法改變這個狀態。這也是Promise這個名字的由來,它的英語意思就是“承諾”,表示其他手段無法改變。

2.一旦狀態改變,就不會再變,任何時候都可以得到這個結果。Promise對象的狀態改變,只有兩種可能:從pending變為fulfilled和從pending變為rejected

let fs = require(‘fs‘);function read(url){    return new Promise((resolve, reject)=>{        fs.readFile(url, ‘utf8‘, (err, data)=>{            if(err) reject(err);            resolve(data);        })    })}let school = {};read(‘./name.txt‘).then(data=>{    school[‘name‘] = data;    return read(‘age.txt‘);}).then(data=>{    school[‘age‘] = data;    console.log(school);//{ name: ‘cjw‘, age: ‘18‘ }})
2.promise用法與原理2.1 Promise.prototype.then()

Promise 執行個體具有then方法,也就是說,then方法是定義在原型對象

//let Promise = require(‘./promise.js‘);let p = new Promise((resolve, reject)=>{    setTimeout(function(){             reject(‘成功‘);    },100)    reject(‘3‘);})p.then((value)=>{    console.log(value);//3,這裡是3因為,只能從一個狀態panding到另一個狀態}, (reason)=>{    console.log(reason);})

基本概念

1.new Promise時需要傳遞一個executor執行器,執行器會立刻執行

2.執行器中傳遞了兩個參數 resolve成功的函數 他調用時可以傳一個值 值可以是任何值 reject失敗函式 他調用時可以傳一個值 值可以是任何值

3.只能從pending態轉到成功或者失敗

4.promise執行個體。每個執行個體都有一個then方法,這個方法傳遞兩個參數,一個是成功另一個是失敗

5.如果調用then時 發現已經成功了會讓成功函數執行並且把成功的內容當作參數傳遞到函數中

6.promise 中可以同一個執行個體then多次,如果狀態是pengding需要將函數存放起來 等待狀態確定後 在依次將對應的函數執行 (發布訂閱)

7.如果類執行時出現了異常 那就變成失敗態

Promise.prototype.then()的實現

function Promise(executor){    var self = this;    self.status = ‘pending‘;//從pending 轉換為resolved rejected    self.value = undefined;    self.reason = undefined;    self.onResolved = [];//專門存放成功的回調    self.onRejected = [];//專門存放失敗的回調    //pending -> resolved    function resolve(value){        if(self.status === ‘pending‘){            self.value = value;            self.status = ‘resolved‘;            self.onResolved.forEach(fn=>fn());        }    }     //pending -> rejected    function reject(reason){        if(self.status === ‘pending‘){            self.reason = reason;            self.status = ‘rejected‘;            self.onRejected.forEach(fn=>fn());        }    }    try{        executor(resolve, reject);    }catch(e){        reject(e);    }}//then方法的實現Promise.prototype.then = function(onfulfilled, onrejected){   let self = this;   if(self.status === ‘resolved‘){//判斷狀態,resolved時候,返回value       onfulfilled(self.value);   }   if(self.status === ‘rejected‘){//判斷狀態,rejected時候,返回reason       onrejected(self.reason);   }   if(self.status === ‘pending‘){      self.onResolved.push(function(){          onfulfilled(self.value);      })      self.onRejected.push(function(){        onfulfilled(self.reason);      })   }}module.exports = Promise;
2.2 Promise.prototype.catch()

Promise.prototype.catch方法.then(null, rejection)的別名,用於指定發生錯誤時的回呼函數。

let p = new Promise((resolve, reject)=>{    resolve();})p.then(data=>{    throw new Error();}).then(null).catch(err=>{    console.log(‘catch‘, err)}).then(null, err=>{    console.log(‘err‘, err);})

實現原理

Promise.prototype.catch = function(onrejected){    return this.then(null, onrejected);}
2.3 Promise.all

Promise.all方法用於將多個 Promise 執行個體,封裝成一個新的 Promise 執行個體。

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

實現原理

Promise.all = function (promises) {    return new Promise((resolve, reject) => {        let results = []; let i = 0;        function processData(index, data) {        results[index] = data; // let arr = []  arr[2] = 100        if (++i === promises.length) {            resolve(results);        }        }        for (let i = 0; i < promises.length; i++) {        let p = promises[i];        p.then((data) => { // 成功後把結果和當前索引 關聯起來            processData(i, data);        }, reject);        }    })}
2.4 Promise.race

Promise.race方法同樣是將多個 Promise 執行個體,封裝成一個新的 Promise 執行個體。

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

上面代碼中,只要p1p2p3之中有一個執行個體率先改變狀態,p的狀態就跟著改變。那個率先改變的 Promise 執行個體的傳回值,就傳遞給p的回呼函數。

let Promise = require(‘./e2.promise‘);let fs = require(‘mz/fs‘);Promise.race([    fs.readFile(‘./age.txt‘, ‘utf8‘),    fs.readFile(‘./name.txt‘, ‘utf8‘)]).then(data=>{    console.log(data);})

實現原理

Promise.race = function(promises){    return new Promise((resolve, reject)=>{        for(let i=0; i< promises.length; i++){            let p = promises[i];            p.then(resolve, reject);        }    })}
2.5 Promise.resolve

Promise.resolve方法允許調用時不帶參數,直接返回一個resolved狀態的 Promise 對象。

const p = Promise.resolve();p.then(function () {  // ...});

實現原理

Promise.resolve = function(value){    return new Promise((resolve, reject)=>{        resolve(value);    })}
2.6 Promise.reject

Promise.reject方法允許調用時不帶參數,直接返回一個rejected狀態的 Promise 對象。

const p = Promise.reject();p.then(function () {  // ...});

實現原理

Promise.reject = function(reason){    return new Promise((resolve, reject)=>{        reject(reason);    })}
2.7 promise的一些擴充庫

bluebird

mz

2.8 應用 async + await = generator + co

generator 生產迭代器的

產生器函數 * generator 一般配合 yield

function * read() {    yield 1;    yield 2;    yield 3;    return 100}let it = read();console.dir(it.next());console.dir(it.next());console.dir(it.next());console.dir(it.next());//結果:{ value: 1, done: false }{ value: 2, done: false }{ value: 3, done: false }{ value: 100, done: true }

promise + generator

let fs = require(‘mz/fs‘);// let co = require(‘co‘);function * read(){    let age = yield fs.readFile(‘./age.txt‘, ‘utf8‘);    return age;}//co 原理function co(it){    return new Promise((resolve, reject)=>{        function next(data){            let { value, done } = it.next(data);            if(!done){                value.then(data=>{                    next(data);                }, reject)            }else{                resolve(value);            }        }        next();    })}co(read()).then(data=>{    console.log(data);//18}, err=>{    console.log(err);})

async + await是es7的文法

let fs = require(‘mz/fs‘);//這個mz庫將nodejs裡面的fs全部函數都promise化// async 函數就是promise es7// 回調的問題 不能try/catch 並發問題async function read() {     let age = await fs.readFile(‘name.txt‘,‘utf8‘)    return age}read().then(data=>{  console.log(data);//cjw})
3.手寫一個promise A+

promise A+ 規範傳送門

測試代碼是否符合a+ 規範 為了讓其能測試

npm install promises-aplus-tests -gpromises-aplus-tests 檔案名稱 可以測試
/* * @Author: caijw  * @Date: 2018-10-01 15:04:43  * @Last Modified by: caijw * @Last Modified time: 2018-10-08 22:41:06 */function Promise(executor){    var self = this;    self.status = ‘pending‘;//從pending 轉換為resolved rejected    self.value = undefined;    self.reason = undefined;    self.onResolved = [];//專門存放成功的回調    self.onRejected = [];//專門存放失敗的回調    //pending -> resolved    function resolve(value){        if(self.status === ‘pending‘){            self.value = value;            self.status = ‘resolved‘;            self.onResolved.forEach(fn=>fn());        }    }     //pending -> rejected    function reject(reason){        if(self.status === ‘pending‘){            self.reason = reason;            self.status = ‘rejected‘;            self.onRejected.forEach(fn=>fn());        }    }    try{        executor(resolve, reject);    }catch(e){        reject(e);    }}//這裡主要就是遞迴迴圈,判斷是否為promise,如果是promise就繼續遞迴迴圈下去。function resolvePromise(promise2, x, resolve, reject){    if(promise2 === x){        return reject(new TypeError(‘循環參考‘));    }    let called;    if(x!=null && (typeof x === ‘object‘ || typeof x === ‘function‘)){        try{            let then = x.then;            //假設他是一個promise,then方法就是一個函數            if(typeof then === ‘function‘){                then.call(x, (y)=>{                    if(called) return;                    called = true;                    // 遞迴解析 如果resolve的是一個promise 就要不停的讓resolve的結果進行處理                    resolvePromise(promise2, y, resolve, reject);                },(e)=>{                    if(called) return;                    called = true;                    reject(e);                })            }else{//不是就返回                resolve(x);            }        }catch(e){            if(called) return;            called = true;            reject(e);        }            }else{        resolve(x);    }}//至返回錯誤的 catch 就是不寫成功的回調的then方法Promise.prototype.catch = function(onrejected){    return this.then(null, onrejected);}//1.解決輸出的順序的問題// all方法的參數 是一個數組,會按照數組的結果放到成功的回調裡(只有全成功才算成功)// race方法參數也是一個數組。會同時發起並發,但是以返回最快的結果為結果Promise.race = function(promises){    return new Promise((resolve, reject)=>{        for(let i=0; i< promises.length; i++){            let p = promises[i];            p.then(resolve, reject);        }    })}Promise.reject = function(reason){    return new Promise((resolve, reject)=>{        reject(reason);    })}Promise.resolve = function(value){    return new Promise((resolve, reject)=>{        resolve(value);    })}Promise.all = function (promises) {    return new Promise((resolve, reject) => {        let results = []; let i = 0;        function processData(index, data) {        results[index] = data; // let arr = []  arr[2] = 100        if (++i === promises.length) {            resolve(results);        }        }        for (let i = 0; i < promises.length; i++) {        let p = promises[i];        p.then((data) => { // 成功後把結果和當前索引 關聯起來            processData(i, data);        }, reject);        }    })}//回呼函數Promise.prototype.then = function(onfulfilled, onrejected){    // onfulfilled / onrejected是一個可選的參數    onfulfilled = typeof onfulfilled == ‘function‘ ? onfulfilled :  val=>val;    onrejected = typeof onrejected === ‘function‘ ? onrejected :err => {        throw err;    }   let self = this;      let promise2;   promise2 = new Promise((resolve, reject)=>{        if(self.status === ‘resolved‘){            setTimeout(()=>{                try{                    let x = onfulfilled(self.value);                    resolvePromise(promise2, x, resolve, reject);                }catch(e){                    reject(e);                }            }, 0)        }        if(self.status === ‘rejected‘){            setTimeout(()=>{                try{                    let x = onrejected(self.reason);                    resolvePromise(promise2, x, resolve, reject);                }catch(e){                    reject(e);                }            }, 0)                    }        if(self.status === ‘pending‘){            self.onResolved.push(function(){                setTimeout(()=>{                    try{                        let x = onfulfilled(self.value);                        resolvePromise(promise2, x, resolve, reject);                    }catch(e){                        reject(e);                    }                }, 0)            })            self.onRejected.push(function(){                setTimeout(()=>{                    try{                        let x = onrejected(self.reason);                        resolvePromise(promise2, x, resolve, reject);                    }catch(e){                        reject(e);                    }                }, 0)            })        }   })   return promise2;}// 文法糖 簡化問題 嵌套的問題 ,被廢棄了Promise.defer = Promise.deferred = function(){    let dfd = {};    dfd.promise = new Promise((resolve, reject)=>{        dfd.resolve = resolve;        dfd.reject = reject;    })    return dfd;}module.exports = Promise;
參考文檔

ECMAScript 6 入門 Promise--阮一峰

Hey, 你的Promise

ES6版Promise實現,給你不一樣的體驗

使用 Bluebird 開發非同步 JavaScript 程式

Promise原理講解 async+await應用(非同步回調解決方案)

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.