ES6 - Promise详解

在了解Promise之前,我们来增加一些基础知识。

在前端中,异步是JS的重要特点,可以说没有异步处理,就没有前后端分离这一说。固然异步处理有异步处理的好处,比如:防止单线程阻塞的问题等。但是异步也会给我们带来一些问题。所以这个时候就需要异步的同步化。问题来了,请问异步同步化的方法有几种?

  • 回调函数(容易导致回调地狱)
  • Promise
  • Generator (微任务)
  • async/await

接下来,我们来聊聊什么Promise,以及Promise是如何处理异步同步化问题的。

Promise 对象

为了解决异步问题,在ES6中加入了一个新的对象Promise。Promise提供了一种更合理、更强大的异步解决方案。告别回调地狱

用于存放未来某个时间才会结束的操作。可以获取异步操作的消息。

Q1: 没有异步就不需要promise?

Q2: promise本身不是异步,只是编写异步的一种容器?

promise 是ES6 主要处理异步的方法,可以将异步操作同步化,安装一定的顺序去执行,符合我们需求的效果。

目的主要是为了防止页面冻结

之前的处理的异步同步化的问题基本上都是使用回调函数,但是很容易进入回调地狱并且代码难维护

注意:promise 是一个对象而不是一个函数的原因是: 对象可以保存状态二函数不行,同时也不会剥夺return的能力。

使用Promise() 函数的缺点
  • 首先,无法取消Promise,一旦新建它就会立即执行,无法中途取消。
  • 其次,如果不设置回调函数,Promise内部抛出的错误,不会反应到外部。
  • 第三,当处于pending状态时,无法得知目前进展到哪一个阶段(刚刚开始还是即将完成)。

下面的例子说明以下区别:

$.ajax({
	url: '/index',
	type: 'get',
	dataType: 'json',
	success: function (data1) {
	    $.ajax({
	        url: '/home',
	        type: 'get',
	        dataType: 'json',
	        data: data1,
	        success: function (data2) {
	            $.ajax({
	                url: '/about',
	                type: 'get',
	                dataType: 'json',
	                data: data2,
	                success: function (data3) {
	                    $.ajax({
	                        url: '/me',
	                        type: 'get',
	                        dataType: 'json',
	                        data: data3,
	                        success: function (data4) {
	                            console.log(data4)
	                        }
	                    })
	                }
	            })
	        }
	    })
	}
})
  • 使用Promise对象方式
new Pormise(resolve = > {
    $.ajax({
        url: '/index',
        type: 'get',
        dataType: 'json',
        success:function (data1) {
            resolve(data1)
        }
    })
})
.then(data1 => {
    $.ajax({
        url: '/home',
        type: 'get',
        dataType: 'json',
        data: data1,
        success: function (data2) {
            resolve(data2)
        }
    })
})
.then(data2 => {
     $.ajax({
        url: '/about',
        type: 'get',
        dataType: 'json',
        data: data2,
        success: function (data3) {
            resolve(data3);
        }
    )}
})
.then(data3 => {
    $.ajax({
        url: '/me',
        type: 'get',
        dataType: 'json',
        data: data3,
        success: function (data4) {
            console.log(data4)
        }
    })
})

Promise 的三种状态

  • pending : 等待状态

    比如正在网络请求,或定时器没有到时间, 可以迁移到执行态或拒绝态

  • fulfill : 成功状态

    当我们主动调用了resolve时, 就处于该状态,并且会回调then函数,

    不能迁移到其他任何的状态,必须拥有一个不可变的终值

  • reject : 失败状态

    当我们主动调用reject时候,就处于该状态, 并且回调catch函数

    不能迁移至其他任何状态, 必须拥有一个不可变的拒因

注意:当promise状态发生改变,就会触发then()里的响应函数处理后续步骤;promise状态一经改变,不会再变。

Pormise 的两个事件

针对3种状态,只有两种转换的方向:

  • pending -> fulfill 成功
  • pending -> reject 失败

在状态装换的时候,就会触发事件:

  • pending -> fulfill 触发 onFulFilled事件(resolve函数)
  • pending -> reject 触发 onRejected 事件(reject函数)

在调用resolve 方法或者reject 方法的时候,就一定会触发事件

需要注册 onFulFilled 事件 和 onRejected 事件

针对事件的注册,提供了then 方法, 如下:

promise.then(onFulFilled, onRejected)

注: 有异步操作的结果,可以决定当前是哪一种状态,任何其他的操作都无法改变这个状态, 一定是状态的变化才能触发事件

基本使用

// () => {} 叫执行器, 会立即执行
// 创建Promise,会默认的处于Pending状态,执行器会立即执行
//promise函数中的事件处理完成之后做了两件事:
// 1. 改变状态 pending -> resolve/reject 并且调用对应的回调函数
// 2. 将执行的结果传递给回调函数作为参数
let p = new Promise(() => {});  
console.log(p); // pending

// 在执行器中需要传入两个参数 分别为resolve 和 reject, 
// pending=>fulfilled(成功) pending=>rejected(失败)
let p = new Promise((resolve, reject) => {
    resolve("有钱了");  
    reject("没钱了"); // 不执行
})
 p.then(() => {
    console.log('成功了...');
},() => {
    console.log('失败了...');
})
console.log(p);

函数then()

函数catch 是Promise中的一个方法,它会在Promise处于reject状态时调用触发

  • 每个.then 返回一个独立的promise对象
  • 每个return的返回值被下一个处理函数的参数接受
  • 支持链式调用
  • then() 执行 可以接受new promise 中异步执行结束的信号。
new Promise((resolve, reject) => {
    setTimeout(() => {
        //在Promise调用reject函数,会使Promise变为 reject 状态
		//reject函数可以传入一个参数,作为catch函数的默认传入参数
        resolve('成功'); // 调用reolve时,可以把成功的结果传递下去
        reject('失败'); // 调用reject时,可以把失败的原因传递下去 ,不执行
    },100)
    // then 方法中两个参数
    // 第一个参数表示从等待状到成功态,会调用第1个参数
    // 第二个参数表示从等待状到失败态,会调用第2个参数
}).then(res =>{
    console.log('有钱了...,因为' + res);
}, res =>{
    console.log('没钱了...' + res);
})

// 处理失败的对象可以调用catch捕捉,同样,也可以通过throw错误对象 抛出一个错误对象,即失败
// throw 语句用于抛出一个用于抛出一个用户自定义的异常,当前函数的执行将被停止(throw之后的语句将不会执行),并且控制将被传递到调用堆栈中的第一个catch块,如果调用者函数没有catch块,程序将会终止。
function getRectArea(width, height) {
    if(isNaN(width) || isNaN(height)) {
        throw "Parameter is not a number!"
    }
}
try {
    getRectArea(3, 'A');
}
catch(e) {
    console.log(e);
}
//.catch(err => {
//    console.lgo(err);
//})
// 结果输出: 有钱了...因为成功

函数finally()

函数finally是Promise中的一个方法,它会在Promise 的最后触发, 无论Promise处于什么状态。

new Promise((resolve, reject) => {
    setIimeout (() => {
        resolve('成功了');
    }, 1000)
})
.then(data => {
    console.log(data);
})
/*
  .catch(err => {
     console.log(err);
  })
*/
.finally(() => {
    consoel.log('Promise结束');
})
/*结果输出: 成功啦
		   Promise结束
*/

函数all()

函数all() 是Promise() 中的一个方法, 他用于将多个Promise实例,包装成一个新的Promise实例

一假为假

Promise.all([new Promise((resolve, reject) => {
		setTimeout(() => {
//等待9个异步任务都成功之后, 提示上传成功; 所有都成功才执行才会去改变状态并且去触发.then
			for(let i = 0; i < 9; i++){
        console.log('异步程序',i);
        resolve(i);
      }
		},3000)
	}),
	new Promise((resolve, reject) => {
		setTimeout(() => {
			resolve('我是第二个异步请求返回的数据')
		},1000)
	})
])
.then(results => {
console.log(results)
})
// 适合案例,并发业务,同时发送多个请求。
// ['我是第一个异步请求返回的数据', '我是第二个异步请求返回的数据']

函数 race()

将多个任务包含在Promise实例中, 只有有一个状态发生改变,就是改变Promise实例的状态。触发.then。

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

检测第一个实例的状态为整个实例的状态并返回给.then

promise 本身是同步的

console.log('start');
let p = new Promise(() => {
    console.log('haha');
})
console.log('end');
// start haha end

但是then方法中的代码只有调用resolve, reject时候才会执行

let p = new Promise((resolve,reject)=>{
    setTimeout(()=>{
        console.log("setTimeout")
        // resolve("有钱了...")
        reject("没钱了...")
    },1000)
})
p.then((suc)=>{
    console.log(suc)  // 有钱了...
},(err)=>{
    console.log(err)  // 没钱了...
})
// 运行结果: setTimeout 没钱了

注意:调用resolvereject并不会终结 Promise 的参数函数的执行。这是因为立即 resolved 的 Promise 是在本轮事件循环的末尾执行,总是晚于本轮循环的同步任务。 所以有个特点是: 本轮的回调函数不会在本轮的事件循环中执行结束。

Promise.prototype.catch()

在链式调用.then中,只要存在一个失败,或错误,就会触发。捕获到错误。

Promise.any()

该方法接受一组 Promise 实例作为参数,包装成一个新的 Promise 实例返回。只要参数实例有一个变成fulfilled状态,包装实例就会变成fulfilled状态;如果所有参数实例都变成rejected状态,包装实例就会变成rejected状态。

一真为真

Q1: Promise.all 和 Promise.race的区别?

Q2: Promise.any 和 Promise.race的区别?

Promise.any()Promise.race()方法很像,只有一点不同,就是不会因为某个 Promise 变成rejected状态而结束。

阅读Promise 编写的代码

Step1: 看then触发是哪一个Promise

Step2: promise 是怎么产生的?

结论: then 的回参 是如何传递的了?

Q1: 产生Promise的方式?

 let p1 = Promise.resolve('1');
  console.log(p1); //Promise对象
  let p2 = Promise.all([1, 2]);
  console.log(p2); //Promise对象
  let p3 = Promise.race([1, 2]);
  console.log(p3); //Promise对象
  let p4 = new Promise((resolve) => {resolve('4') })
  console.log(p4);
  let p5 = p4.then((res) => { return res })
  console.log(p5);

// 注意所有的Promise 都可以触发 .then回调函数
// 1. Promise.resolve(data) 中 then 的回参是 resolve 的实参data
  p1.then(res => {
      console.log(res); //1
  })

  // 2. all 是所有成功promise 的 resolve()实参
  p2.then(res => {
      console.log(res); //[1,2]
  })

  // 3. race 是第一个变为fulfilled状态的 promise 的 resolve() 实参
  p3.then(res => {
      console.log(res);//[1,2]
  })

  // 4. resolve() 实参
  p4.then(res => {
      console.log(res); //4
  })
  // 5. then 中的 return 返回值
  p5.then(res => {
      console.log(res); // 4
  })

接下来,我们使用promise来模拟一下ajax来请求和发送数据。

使用promise封装Ajax
function $ajax({ url = '/', method = 'GET', data = {} }) {
    return new Promise((resolve, reject) => {
        var params = '';
        for (var x in data) {
            params += `${x}=${data[x]}&`
        }
        params = params.slice(0, params.length - 1);
        method = method.toUpperCase();

        var xhr = new XMLHttpRequest();
        if (method === 'GET') {
            url = url + '?' + params;
            xhr.open(method, url, true);
            xhr.send();
        }
        if (method === 'POST') {
            xhr.open(method, url, true)
            xhr.setRequestHeader("content-type", "application/x-www-form-urlencoded");
            xhr.send(params)
        }

        xhr.onreadystatechange = function (data) {
            setTimeout(function () {
                if (xhr.readyState === 4 && xhr.status == 200) {
                    resolve(xhr.responseText)
                } else {
                    reject(data)
                }
            }, 1000)
        }
    })
}

第一种方式:链式调用

<script>
  function getDataView(query) {
            $ajax({ url: 'http://localhost:3000/', data: query }).then(api => {
                console.log(api, 'api');
                return api
            }).then(store=>{
                console.log(store,'store');
                return store
            }).then(view=>{
                console.log(view,'html');
            })
        }
        getDataView({aa:11}) //执行
script>

第二种方式: 函数的方式

<script>
     function getData(query){
         return $ajax({ url: 'http://localhost:3000/', data: query }).then(api => {
             console.log(api, 'api');
             return api
         })
     }
     function getDataStore(query){
         return getData(query).then(store=>{
             console.log(store,'store');
             return store
         })
     }
     function getDataView(query) {
         getDataStore(query).then(view=>{
             console.log(view,'html');
         })
     }
     getDataView({aa:11})
script>

第三种方式: 文件拆分的方式

// index.html
function getDataView() {
      console.log('请求已经成功接受了');
      return getDataStore({aa:11}).then(res => {
          console.log(res,'html');
      })
  }
  getDataView(); //执行

//store.js
function getDataStore(query) {
    return getData(query).then(store => {
        console.log('getDataStore',store);
        return store
    })
}


//api.js
function getData(query) {
    return new Promise((resolve, reject) => {
        // resolve('getData');
        return $ajax({ url: 'http://localhost:3000/', data: query }).then(api => {
            console.log('getData', api);
            resolve(api);
        }).then(res => {
            resolve(res);
        })
    })
    // 返回一个.then 返回的Promise对象
}
    //api 发请求,不处理结果。将结果传递出去
    //store 接受响应结果,并将结果传递出去
    //index 接受store 输出结果(响应结果),并处理结果(与视图相互结合)

最后贴上serve.js代码

const http = require('http');
const serve = http.createServer((req, res) => {
    console.log('111');
    res.writeHead(200, {
        'Access-Control-Allow-Origin': '*',
        "Access-Control-Allow-Methods": "*",
        "Access-Control-Allow-Headers": "Content-Type,XFILENAME,XFILECATEGORY,XFILESIZE,X-URL-PATH,x-access-token"
    })
    res.end('hellow word')
})

serve.listen(3000)

你可能感兴趣的:(Javascript,js原型,es6)