Promise入门_1

1. 预备知识

1.1 实例对象与函数对象

  • 实例对象:new 函数产生的对象,称为实例对象,简称为对象
  • 函数对象:将函数作为对象使用时,称为函数对象
function Fn() { // Fn只能称为函数
}
const fn = new Fn() // Fn只有new过的才可以称为构造函数
//fn称为实例对象
console.log(Fn.prototype)// Fn作为对象使用时,才可以称为函数对象
Fn.bind({}) //Fn作为函数对象使用
$('#test') // $作为函数使用
$.get('/test') // $作为函数对象使用

()左边是函数,点(。)左边是对象(函数对象、实例对象)


1.2 两种类型的回调函数

1. 同步回调

立即执行,完全执行完了才结束,不会放入回调队列中。
数组遍历相关的回调 / Promise的executor函数

const arr = [1, 3, 5];
arr.forEach(item => { // 遍历回调,同步回调,不会放入队列,一上来就要执行
  console.log(item);
})
console.log('forEach()之后')

image

2. 异步回调

不会立即执行,会放入回调队列中将来执行。
定时器回调 / ajax回调 / Promise成功或失败的回调。

// 定时器回调
setTimeout(() => { // 异步回调,会放入队列中将来执行
  console.log('timeout callback()')
}, 0)
console.log('setTimeout()之后')

image

// Promise 成功或失败的回调
new Promise((resolve, reject) => {
  resolve(1)
}).then(
  value => {console.log('value', value)},
  reason => {console.log('reason', reason)}
)
console.log('----')
// ----
// value 1

js 引擎先把初始化的同步代码都执行完成后,才执行回调队列中的代码


1.3 JS中的异常error处理

1. 错误的类型

Error:所有错误的父类型。

ReferenceError:引用的变量不存在。
console.log(a) // ReferenceError:a is not defined

TypeError:数据类型不正确。

let b
console.log(b.xxx)
// TypeError:Cannot read property 'xxx' of undefined

let c = {}
c.xxx()
// TypeError:c.xxx is not a function

RangeError:数据值不在其所允许的范围内。

function fn() {
  fn()
}
fn()
// RangeError:Maximum call stack size exceeded

SyntaxError:语法错误

const c = """"
// SyntaxError:Unexpected string

2. 错误处理(捕获与抛出)

抛出错误:throw error

function something() {
  if (Date.now()%2===1) {
    console.log('当前时间为奇数,可以执行任务')
  } else { //如果时间为偶数抛出异常,由调用来处理
    throw new Error('当前时间为偶数,无法执行任务')
  }
}

捕获错误:try ... catch

// 捕获处理异常
try {
  something()
} catch (error) {
  alert(error.message)
}

3. 错误对象

massage 属性:错误相关信息
stack 属性:函数调用栈记录信息

try {
  let d
  console.log(d.xxx)
} catch (error) {
  console.log(error.message)
  console.log(error.stack)
}
console.log('出错之后')
// Cannot read property 'xxx' of undefined
// TypeError:Cannot read property 'xxx' of undefined
// 出错之后

因为错误被捕获处理了,后面的代码才能运行下去,打印出‘出错之后’



2. Promise的理解和使用

2.1 Promise是什么

1. 理解Promise

  • 抽象表达:Promise是JS中进行异步编程的新的解决方案(旧方案是单纯使用回调函数)
    ---- 异步编程 ①fs 文件操作 ②数据库操作 ③Ajax ④定时器
    【推荐阅读 【JavaScript】同步与异步-异步与并行-异步运行机制-为什么要异步编程-异步与回调-回调地狱-JavaScript中的异步操作】

  • 具体表达:
    ①从语法上看:Promise是一个构造函数 (自己身上有all、reject、resolve这几个方法,原型上有then、catch等方法)
    ②从功能上看:promise对象用来封装一个异步操作并可以获取其成功/失败的结果值。

  • 阮一峰的解释:
    所谓Promise,简单说就是一个容器,里面保存着某个未来才会结束的事件(通常是一个异步操作)的结果。
    从语法上说,Promise 是一个对象,从它可以获取异步操作的消息。
    Promise 提供统一的 API,各种异步操作都可以用同样的方法进行处理。

2. Promise 的状态

实例对象promise中的一个属性 PromiseState
Promise 的状态:pending:未决定的、resolved:成功、rejected:失败。
Promise 的状态改变只有如下两种:

  1. pending 变为 resolved/fullfilled

  2. pending 变为 rejected

注意:

  • throw也会改变promise的状态。
  • 对象的状态不受外界影响。
  • 只有这两种,且一个 promise 对象的状态只能改变一次。
  • 一旦状态改变,就不会再变,任何时候都可以得到这个结果。
  • 无论成功还是失败,都会有一个结果数据。成功的结果数据一般称为 value,而失败的一般称为 reason。

3. Promise对象的值

实例对象promise的另一个值 PromiseResult
它是实例对象promise的一个属性。
保存着对象异步任务的结果: 成功/失败 的值(value/reason

resolve/reject这两个函数可以修改PromiseResult属性的值

4. Promise 的基本流程

image

5. Promise 的基本使用

const promise = new Promise(function(resolve, reject) {
  // ... some code
  if (/* 异步操作成功 */){
    resolve(value);
  } else {
    reject(reason);
  }
});

Promise构造函数接受一个函数(执行器函数)作为参数,该函数的两个参数分别是resolvereject。它们是两个函数,由 JavaScript 引擎提供,不用自己部署。

resolve函数的作用是,将Promise对象的状态从“未完成”变为“成功”(即从 pending 变为 resolved),在异步操作成功时调用,并将异步操作的结果,作为参数value传递出去;

reject函数的作用是,将Promise对象的状态从“未完成”变为“失败”(即从 pending 变为 rejected),在异步操作失败时调用,并将异步操作报出的错误,作为参数error/reason传递出去。

Promise实例生成以后,可以用then方法分别指定resolved状态和rejected状态的回调函数。

promise.then(function(value) {
  // success
}, function(reason) {
  // failure
});

then方法可以接受两个回调函数作为参数。
第一个回调函数onResolved()Promise对象的状态变为resolved时调用
第二个回调函数onRejected()Promise对象的状态变为rejected时调用
这两个函数都是可选的,不一定要提供。它们都接受Promise对象传出的值作为参数。

  • 一个例子
// 创建一个新的p对象promise
const p = new Promise((resolve, reject) => { // 执行器函数
  // 执行异步操作任务
  setTimeout(() => {
    const time = Date.now() 
    // 如果当前时间是偶数代表成功,否则失败
    if (time % 2 == 0) {
      // 如果成功,调用resolve(value)
      resolve('成功的数据,time=' + time)
    } else {
      // 如果失败,调用reject(reason)
      reject('失败的数据,time=' + time)
    }
  }, 1000);
})

p.then(
  value => { // 接收得到成功的value数据 onResolved
    console.log('成功的回调', value)  // 成功的回调 成功的数据,time=1615015043258
  },
  reason => { // 接收得到失败的reason数据 onRejected
    console.log('失败的回调', reason)    // 失败的回调 失败的数据,time=1615014995315
  }
)

.then() 和执行器(executor)同步执行,.then() 中的回调函数异步执行。
执行器就是Promise构造函数的那个匿名函数参数。



2.2 为什么要用 Promise

1.指定回调函数的方式更加灵活

旧的:必须在启动异步任务前指定,如:

// 1. 纯回调的形式
// 成功的回调函数
function successCallback(result) {
  console.log("声音文件创建成功:" + result);
}
// 失败的回调函数
function failureCallback(error) {
  console.log("声音文件创建失败:" + error);
}
// 必须先指定回调函数,再执行异步任务
createAudioFileAsync(audioSettings, successCallback, failureCallback) // 回调函数在执行异步任务(函数)前就要指定

promise:启动异步任务 => 返回promise对象 => 给promise对象绑定回调函数(甚至可以在异步任务结束后指定)

// 2. 使用Promise
const promise = createAudioFileAsync(audioSettings);  // 执行2秒
setTimeout(() => {
  promise.then(successCallback, failureCallback) // 也可以获取
}, 3000);

2.支持链式调用,可以解决回调地狱问题

什么是回调地狱?
回调函数嵌套调用,外部回调函数异步执行的结果是其内部嵌套的回调函数执行的条件。如:

doSomething(function(result) {
  doSomethingElse(result, function(newResult) {
    doThirdThing(newResult, function(finalResult) {
      console.log('Got the final result:' + finalResult)
    }, failureCallback)
  }, failureCallback)
}, failureCallback)

回调地狱的缺点?
不便于阅读,不便于异常处理。

解决方案?
promise 链式调用。promise是一个构造函数,他本身是同步执行的,只不过他身上的then()方法是异步执行的。promise和ajax没有必然联系,它只是用来封装异步操作而已

使用 promise 的链式调用解决回调地狱:

doSomething()
  .then(result => doSomethingElse(result))
  .then(newResult => doThirdThing(newResult))
  .then(finalResult => {console.log('Got the final result:' + finalResult)})
  .catch(failureCallback)

终极解决方案?
async/await
回调地狱的终极解决方案 async/await

async function request() {
  try{
    const result = await doSomething()
    const newResult = await doSomethingElse(result)
    const finalResult = await doThirdThing(newResult)
    console.log('Got the final result:' + finalResult)
  } catch (error) {
    failureCallback(error)
  }
}

2.3 如何使用 Promise

1. Promise 构造函数:Promise(executor) {}

  • executor 函数:同步执行 (resolve, reject) => {}
  • resolve 函数:内部定义成功时调用的函数 resove(value)
  • reject 函数:内部定义失败时调用的函数 reject(reason)

说明:executor 是执行器,会在 Promise 内部立即同步回调,异步操作 resolve/reject 就在 executor 中执行。执行器指的就是Promise的那个带两个形参的匿名函数。
如:

        let p=new Promise((resolve,reject)=>{
            //一般来说回调函数是执行完所有代码后执行的(异步),这里111在222前面输出证明是同步
            console.log(222);            
            resolve();//改变promise实例的值。调用的then是异步的。
        });

        p.then(value=>{
            //promise里面的函数是同步,它的then方法是异步
            //promise对象的then方法是异步执行的,也就是属于回调代码,是等初始化代码执行完后才执行的。
            console.log(333);
        });

        console.log(111);

输出顺序如下:
image

2. Promise.prototype.then 方法:p.then(onResolved, onRejected)

指定两个回调(成功+失败)

onResolved 函数:成功的回调函数 (value) => {}
onRejected 函数:失败的回调函数 (reason) => {}

说明:指定用于得到成功 value 的成功回调和用于得到失败 reason 的失败回调,返回一个新的 promise 对象

练习1:点击按钮, 1s 后显示是否中奖(30%概率中奖)
若中奖弹出 恭喜恭喜, 奖品为 10万 RMB 劳斯莱斯优惠券
若未中奖弹出 再接再厉

        //生产范围的随机数函数
        function rand(x,y){
            return Math.round(Math.random()*(y-x)+x);            
        }

        //获取按钮
        let btn=document.querySelectorAll("button")[0];

        btn.addEventListener("click",function(){  //绑定事件

            //Promise形式实现:
            //resolve:匿名函数参数,是一个函数,表示解决,成功的时候调用。
            //reject:匿名函数参数,是一个函数,表示拒绝,失败的时候调用。
            let p=new Promise((resolve,reject)=>{
                setTimeout(() => {
                    let number=rand(1,100);
                   
                    if(number<=30){
                        resolve();//该函数调用之后会将Promise的实例的状态设置为 “成功”
                    }else{
                        reject();//该函数调用之后会将Promise的实例的状态设置为 “失败”
                    }
                }, 1000);
            });


            //调用then方法,第一个匿名函数是对象状态为成功时的回调,第二个是对象状态为失败时候的回调。
            p.then(()=>{
                alert("你中奖了")
            },()=>{
                alert("再接再厉");
            });
        });

练习2:将练习1中提示框里输出用户的中奖号码,借助resolve和reject两个函数的参数进行传递给它们的回调函数。
只需修改如下代码:

btn.addEventListener("click",function(){  //绑定事件
            let p=new Promise((resolve,reject)=>{
                setTimeout(() => {
                    let number=rand(1,100);
                   
                    if(number<=30){
                        resolve(number)
                    }else{
                        reject(number);
                    }
                }, 2000);
            });

            p.then((value)=>{
                alert("你中奖了,你的中奖数字为:"+value)
            },(reason)=>{
                alert("再接再厉,你的号码为:"+reason);
            });
        });

练习3:用promise封装fs文件操作

const fs=require("fs");

let p=new Promise((resolve,reject)=>{
    fs.readFile("./resource/content.txt",function(err,data){
        //判断是否成功
        if(err) reject(err);
        resolve(data);
    });
});

p.then(value=>console.log(value.toString()),reason=>console.log(reason));

练习4:用promise封装ajax请求操作:
原生写法:
image

封装写法:

image


使用util.promisify方法将要传回调函数的方法进行promise风格转化。

也就是需要用回调函数的可以同promise的then方法来传回调函数。
image


promise封装发送ajax请求

        function sendAjax(url){
            let p=new Promise((resolve,reject)=>{                
                let xhr=new XMLHttpRequest();
                xhr.open("GET",url);
                xhr.send();
                xhr.onreadystatechange=function(){                    
                    if(xhr.readyState==4){
                        if(xhr.status>=200&& xhr.status<300){
                            resolve(xhr.response);
                        }else{
                            reject(xhr.status);
                        }    
                    }
                }
            });
            //返回这个promise的实例对象
            return p;
        }

调用这个返回会返回promise对象的实例,可以通过实例调用then方法设置回调函数。
image


3. Promise.prototype.catch 方法:p.catch(onRejected)

指定失败的回调

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

说明:这是then() 的语法糖,相当于 then(undefined, onRejected)

new Promise((resolve, reject) => { // excutor执行器函数
 setTimeout(() => {
   if(...) {
     resolve('成功的数据') // resolve()函数
   } else { 
     reject('失败的数据') //reject()函数
    }
 }, 1000)
}).then(
 value => { // onResolved()函数
  console.log(value) // 成功的数据
}
).catch(
 reason => { // onRejected()函数
  console.log(reason) // 失败的数据
}
)

4. Promise.resolve 方法:Promise.resolve(value)

value:将被 Promise 对象解析的参数,也可以是一个成功或失败的 Promise 对象

返回:返回一个带着给定值解析过的 Promise 对象,如果参数本身就是一个 Promise 对象,则直接返回这个 Promise 对象。

  1. 如果传入的参数为 非Promise类型的对象, 则返回的结果为成功promise对象
let p1 = Promise.resolve(521);
console.log(p1); // Promise {<fulfilled>: 521}

image

  1. 如果传入的参数为 Promise 对象, 则参数的结果决定了 resolve 的结果
let p2 = Promise.resolve(new Promise((resolve, reject) => {
    // resolve('OK'); // 成功的Promise
    reject('Error');
}));
console.log(p2);
p2.catch(reason => {
    console.log(reason);
})

image

  1. 如果传入的参数也是一个promise实例,那么这个实例的值就决定了resove返回的promise对象的值,即实例的值是成功,那就返回的对象的值也是成功,反之也一样。

5. Promise.reject 方法:Promise.resolve(reason)

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

let p = Promise.reject(521);
let p2 = Promise.reject('iloveyou');
let p3 = Promise.reject(new Promise((resolve, reject) => {
    resolve('OK');
}));

console.log(p);
console.log(p2);
console.log(p3);

image

  • Promise.resolve()/Promise.reject() 方法就是一个语法糖
  • 用来快速得到Promise对象
//产生一个成功值为1的promise对象
new Promise((resolve, reject) => {
 resolve(1)
})
//相当于
const p1 = Promise.resolve(1)
const p2 = Promise.resolve(2)
const p3 = Promise.reject(3)

p1.then(value => {console.log(value)}) // 1
p2.then(value => {console.log(value)}) // 2
p3.catch(reason => {console.log(reason)}) // 3


6. Promise.all 方法:Promise.all(iterable)

iterable:包含 n 个 promise 的可迭代对象,如 Array 或 String
说明:返回一个新的 promise,只有所有的 promise 都成功才成功,只要有一个失败了就直接失败。
全部成功返回成功的值,值里面有每个成功的promise对象的值的数组。

let p1 = new Promise((resolve, reject) => {
  resolve('OK');
})
let p2 = Promise.reject('Error');
let p3 = Promise.resolve('Oh Yeah');

const result = Promise.all([p1, p2, p3]);
console.log(result);

image


7. Promise.race方法:Promise.race(iterable)

iterable:包含 n 个 promise 的可迭代对象,如 Array 或 String

说明:返回一个新的 promise,第一个完成的 promise 的结果状态就是最终的结果状态
谁先完成就输出谁(不管是成功还是失败)
使用场景:ajax请求和超时回调,ajax在超时回调执行前完成,显示回调结果,否则显示请求超时

const pRace = Promise.race([p1, p2, p3])
// 谁先完成就输出谁(不管是成功还是失败)
const p1 = new Promise((resolve, reject) => {
 setTimeout(() => {
   resolve(1)
 }, 1000)
})
const p2 = Promise.resolve(2)
const p3 = Promise.reject(3)

pRace.then(
value => {
   console.log('race onResolved()', value)
 },
reason => {
   console.log('race onRejected()', reason) 
 }
)
//race onResolved() 2
let p1 = new Promise((resolve, reject) => {
  setTimeout(() => {
      resolve('OK');
  }, 1000);
})
let p2 = Promise.resolve('Success');
let p3 = Promise.resolve('Oh Yeah');

//调用
const result = Promise.race([p1, p2, p3]);

console.log(result);

image

posted @   青仙  阅读(80)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· 阿里巴巴 QwQ-32B真的超越了 DeepSeek R-1吗?
· 【译】Visual Studio 中新的强大生产力特性
· 10年+ .NET Coder 心语 ── 封装的思维:从隐藏、稳定开始理解其本质意义
· 【设计模式】告别冗长if-else语句:使用策略模式优化代码结构
点击右上角即可分享
微信分享提示