Promise

1 Promise介绍和使用

1.1Promise是什么?

  • 抽象表达
    Promise是一门新的技术(ES6规范)
    Promise是JS中进行异步编程的新解决方案(旧方案是单纯使用回调函数)
  • 具体表达:
    从语法上来说:Promise是一个构造函数
    从功能上来说:promise对象用来封装一个异步操作,并可以获取其成功/失败的结果值。

异步编程:

 1. fs 文件操作
  	require('fs').readFile('./index.html', (err,data)=>{})
 2. 数据库操作
 3. AJAX
 	 $.get('/server', (data)=>{})
 4. 定时器
  	setTimeout(()=>{}, 2000)

1.2 Promise优势

  • 指定回调函数的方式更加灵活
  • 支持链式调用,可以解决回调地狱问题

1.3 Promise基本使用

const p = new Promise(resolve, reject){
    setTimeout(() => {
        let n = rand(1, 100);
        if(n <= 30)
            resolve(n)
        else
            reject(n)
    }, 2000)
}
p.then((value)=>{
    alert("success")
}, (reason)=>{
    alert("default")
})

1.4 util.promisify(original)

  • original: function
  • 返回:function
  • 传入一个遵循常见的错误优先的回调风格的函数( (err, value)=>{} )
  • 将普通回调函数转换成 promise

1.5 Promise状态

  • pending 未决定的 --> resolved/rejected
  • resolved / fullfilled 成功
  • rejected 失败
  • 说明:只能改变一次
    无论变为成功还是失败,都会有一个结果数据。
    成功的结果数据一般称为value,失败的结果数据一般称为reason。

1.6 Promise对象的值

  • 保存着异步任务 成功/失败 的结果;
  • 可以通过 resolve/reject 进行修改

1.7 Promise 基本流程

promise执行流程

1.8 Promise封装AJAX请求

const p = new Promise((resolve, reject) => {
    const xhr = XMLHTTPRequest();
    xhr.open('GET', 'url');
    xhr.send();
    xhr.onreadystatechange = function(){
        if(xhr.readyState >= 200 && xhr.readyState < 300) {
            resolve(xhr.response)
        }else{
            reject(xhr.status)
        }
    }
});
p.then(value=>{
    console.log(value);
}, reason => {
    console.warn(reason)
})

2 Promise API

2.1 Promise构造函数:Promise(excutor){}

  • executor 函数:执行器 (resolve, reject) =>{}
  • resolve 函数:内部定义成功时我们调用的函数 value=>{}
  • reject 函数:内部定义失败时我们调用的函数 reason=>{}
  • 说明:executor会在Promise内部立即同步调用,异步操作在执行器中执行。

2.2 Promise.prototype.then 方法:(onResolved, onRejected)=>{}

  • onResolved 函数:成功的回调函数 (value)=>{}
  • onRejected函数:失败的回调函数 (reason)=>{}
  • 说明:指定用于得到成功value的成功回调和用于得到失败reason的失败回调,返回一个promise对象。

2.3 Promise.prototype.catch方法:(onRejected)=>{}

  • onRejected函数:失败的回调函数 (reason)=>{}

2.4 Promise.resolve方法:(value)=>{}

  • value:成功的数据或promise对象
  • 返回一个成功/失败的 promise 对象

2.5 Promise.reject方法:(reason)=>{}

  • reason:失败的原因
  • 返回一个失败的promise对象

2.6 Promise.all方法:(promises)=>{}

  • promises:包含n个promise的数组
  • 返回一个新的promise,只有所有的romise都成功才成功,一个失败就直接失败

2.7 Promise.race方法:(promises)=>{}

  • promises:包含n个promise的数组
  • 返回一个新的promise,第一个完成的promise的结果状态就是最终的结果状态

3 Promise 关键问题

3.1 如何改变Promise的状态?

  • resolve(value):如果当前是pending就会变为resolved
  • reject(reason):如果当前是pending就会变为rejected
  • 抛出异常:如果当前是pending就会变成rejected

3.2 一个promise指定多个成功/失败回调函数,都会调用吗?

  • 当promise改变为对应状态时都会调用
let p = new Promise((resolve, reject) => {
    resolve("OK");
})
// 指定回调1
p.then(value => {
    console.log(value); // OK
})

// 指定回调2
p.then(value => {
    console.log(value);  // OK
})

3.3 改变promise状态和指定回调函数谁先谁后?

  • 都有可能,正常情况下是先指定回调再改变状态,但也可以先改状态再指定回调
  • 什么情况下先指定回调再改变状态?
    • 执行器内部是一个异步任务
  • 如何先改状态再指定回调?
    • 在执行器中直接调用 resolve() / reject()
    • 延长更长的时间才调用 then()
  • 什么时候才能得到数据?
    • 如果先指定的回调,那当状态发生改变时,回调函数就会调用,得到数据;
    • 如果先改变状态,那当指定回调时,回调函数就会调用,得到数据。

3.4 promise.then()返回的新的promise的结果状态由什么决定?

  • 简单表述:由then()指定的回调函数执行的结果表示
  • 详细描述:
    • 如果抛出异常,新的promise变为rejected,reason为抛出的异常;
    • 如果返回的是非promise的任意值,新的promise变为resolved,value为返回的值;
    • 如果返回的是另一个新的promise,此promise的结果就会变成新promise的结果。

3.5 promise如何串联多个操作任务?

  • promise的then()返回一个新的promise,可以开启then()的链式调用。
  • 通过then的链式调用串联多个同步/异步任务。
let p = new Promise((resolve, reject) => {
    resolve("OK");
})

p.then(value => {
    return new Promise( (resolve, reject) => {
        resolve("success");
    })
}).then(value => {
    console.log(value); // success
}).then(value => {
    console.log(value);  // undefined
})

3.6 promise异常穿透

  • 当使用 promise 的 then 链式调用时,可以在最后指定失败的回调;
  • 前面任何操作出了异常,都会传到最后失败的回调中处理。
let p = new Promise((resolve, reject) => {
    reject("error");
})

p.then(value => {
    console.log(111);
}).then(value => {
    console.log(222); 
}).then(value => {
    console.log(333);
}).catch(reason => {
    console.warn(reason); // error
})

3.7 中断promise链

  • 当使用promise的then链式调用时,在中间中断,不再调用后面的回调函数
  • 办法:在回调函数中返回一个pendding状态的promise对象。
let p = new Promise((resolve, reject) => {
    resolve("OK");
})

p.then(value => {
    console.log(111);
}).then(value => {
    console.log(222); 
    // 中断:有且只有一种方式
    return new Promise(() => {});
}).then(value => {
    console.log(333);  
}).catch(reason => {
    console.warn(reason);
})

4 Promise自定义封装

git地址:https://github.com/danfeng0205/My-Promise.git

4.1 构造函数

  • 对于同步任务,先修改状态再执行回调;
  • 对于异步任务,由于先指定了回调,然后才开始修改状态,再执行回调。
    因此需要先保存我们的回调(callbacks,因为一个promise可以指定多个成功/失败回调函数且都会执行,因此采用数组进行保存),然后再修改状态(resolve/reject)时再执行回调。
function Promise(executor) {
    // 添加属性
    this.PromiseState = 'pending';
    this.PromiseResult = null;
    // 用于保存异步请求中promise对象
    this.callbacks = [];
    // 由于resolve/reject是直接调用(this指向window)
    // 将this保存,指向该实例对象
    const self = this;
    // resolve函数
    function resolve(data) {
        // 判断状态,保证只执行一次
        if (self.PromiseState != 'pending')
            return
        // 修改对象的状态
        self.PromiseState = "fullfilled";
        // 设置对象的结果值
        self.PromiseResult = data;
        // setTimeout是为了实现then方法中的事件是异步操作
        // 对于异步请求真正执行的时刻
        setTimeout(()=>{
            self.callbacks.forEach(item => {
                item.onResolved(data);
            })
        })
    }

    function reject(data) {
        if (self.PromiseState != 'pending')
            return
        self.PromiseState = "rejected";
        self.PromiseResult = data;
        setTimeout(() => {
            self.callbacks.forEach(item => {
                item.onRejected(data);
            })
        });
    }
    // 对抛出异常的处理
    try {
        executor(resolve, reject);
    } catch (error) {
        reject(error)
    }
}

4.2 then

  • 对于同步任务,先修改状态再执行回调;
  • 对于异步任务,由于先指定了回调(pending),然后才开始修改状态,再执行回调。因此需要判断三种状态(pending、fullfilled、rejected)的情况
    • fullfilled:执行onResolved
    • rejected:执行onRejected
    • pending:此时还未修改状态,将回调保存在callbacks中
Promise.prototype.then = function (onResolved, onRejected) {
    const self = this;
    // 穿透
    if (typeof onRejected !== 'function') {
        onRejected = reason => {
            throw reason;
        }
    }
    if (typeof onResolved !== 'function') {
        onResolved = value => {
            return value;
        }
    }
    return new Promise((resolve, reject) => {
        function callback(type) {
            try {
                let result = type(self.PromiseResult);
                if (result instanceof Promise) {
                    result.then(v => {
                        resolve(v)
                    }, r => {
                        reject(r)
                    })
                } else {
                    // 结果对象状态为成功
                    resolve(result);
                }
            } catch (e) {
                reject(e)
            }
        };
        if (this.PromiseState === 'fullfilled') {
            // 获取回调函数的执行结果
            // setTimeout是为了实现then方法中的事件是异步操作
            setTimeout(()=>{
                callback(onResolved)
            })
        }
        if (this.PromiseState === 'rejected') {
            setTimeout(()=>{
                callback(onRejected)
            })
        }
        if (this.PromiseState === 'pending') {
            // 当状态改变之后,就会执行
            this.callbacks.push({
                onResolved: function () {
                    // 执行成功的回调函数
                    callback(onResolved)
                },
                onRejected: function () {
                    callback(onRejected)
                }
            });
        }
    })
}

4.3 catch

Promise.prototype.catch = function (onRejected) {
    return this.then(undefined, onRejected);
}

4.4 resolve

  • 静态resolve方法:如果返回一个成功/失败的 promise 对象(取决于promise对象执行结果);如果返回一个非promise对象,则状态为成功。
Promise.resolve = function (value) {
    return new Promise((resolve, reject) => {
        if (value instanceof Promise) {
            value.then(v => {
                resolve(v);
            }, r => {
                reject(r)
            })
        } else {
            resolve(value)
        }
    })
}

4.5 reject

  • 静态 reject 方法返回一个失败的promise对象,因此直接调用 reject()。
Promise.reject = function (reason) {
    return new Promise((resolve, reject) => {
        reject(reason)
    })
}

4.6 all

  • 需要声明一个数组存放成功的结果,且为了保证存放的顺序始终一致,需要用arr[i],而不能采用push方法。
  • 当成功的数量(count)和promises数组长度一样时,代表全部成功。
Promise.all = function (promises) {
    // 返回结果为Promise对象
    return new Promise((resolve, reject) => {
        // 声明变量(计数器)
        let count = 0;
        // 存放成功结果
        let arr = [];
        // 遍历
        for (let i = 0; i < promises.length; i++) {
            promises[i].then(v => {
                count++;
                arr[i] = v;
                if (count === promises.length) {
                    resolve(arr);
                }
            }, r => {
                reject(r);
            })
        }
    })
}

4.7 race

Promise.race = function (promises) {
    // 返回结果为Promise对象
    return new Promise((resolve, reject) => {
        // 遍历
        for (let i = 0; i < promises.length; i++) {
            promises[i].then(v => {
                // 修改返回对象的状态为成功
                resolve(v);
            }, r => {
                // 修改返回对象的状态为失败
                reject(r);
            })
        }
    })
}

5 async与await

5.1 async函数

  • 函数的返回值为promise对象
  • promise对象的结果由async函数执行的返回值决定
    • 如果返回一个非promise类型数据,状态为成功,PromiseResult 是返回值;
      async返回非promise对象

    • 如果返回一个Promise对象,由promise对象决定;
      async返回失败的promise
      async返回一个抛出异常的promise
      async返回一个成功的promise

    • 抛出异常,返回一个失败的promise对象,PromiseResult 是返回的值 ;
      async抛出异常

async function main(){
    // 1.返回值如果是一个非promise类型的数据
    // return 521
    // 2.返回一个Promise 对象
    // return new Promise((resolve, reject) => {
    //     // throw 'x'
    //     reject('Error')
    //     resolve('OK')
    // })
    // 3.抛出异常
    throw 'error'
}
let res = main();
console.log(res)

5.2 await表达式

  • await右侧的表达式一般为promise对象,但也可以是其他的值;
  • 如果表达式是promise对象,await返回的是promise成功的值
 let p = new Promise((resolve, reject) => {
    // throw 'x'
    reject('Error')
    // resolve('OK')
})
// 1.promise成功
let res = await p; // OK

// 2.非promise
let res2 = await 20; // 20

// 3.promise失败,需要try catch
//let res3 = await p; // Uncaught (in promise) Error
try {
    let res3 = await p
} catch (error) {
    console.log(error);
}

5.3 注意

  • await 必须写在 async 函数中,但 async 函数中可以没有 await;
  • 如果 await 的 promise 失败了,就会抛出异常,需要通过 try catch 捕获处理
/*
    读取三个文件内容
*/

// 回调函数的方式
const { log } = require('console');
const fs = require('fs');
const util = require('util');
const mineReadFile = util.promisify(fs.readFile)

// 回调地狱
fs.readFile('./resource/1.txt', (err, data1) => {
    if (err) throw err;
    fs.readFile('./resource/2.txt', (err, data2) => {
        if (err) throw err;
        fs.readFile('./resource/3.txt', (err, data3) => {
            if (err) throw err;
            console.log(data1 + data2 + data3);
        })
    })
})

// 改写成async await
async function main() {
    try {
        let data1 = await mineReadFile('./resource/1.txt');
        let data2 = await mineReadFile('./resource/2.txt');
        let data3 = await mineReadFile('./resource/3.txt');
        console.log(data1 + data2 + data3);
    } catch (error) {
        console.log(error);
    }
}
main();
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值