promise及异步编程async await

这篇具有很好参考价值的文章主要介绍了promise及异步编程async await。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

前置说明

ECMAScript 6 新增了正式的 Promise(期约)引用类型,支持优雅地定义和组织异步逻辑。接下来几个版本增加了使用 async 和 await 关键字定义异步函数的机制
JavaScript 是单线程事件循环模型。异步行为是为了优化因计算量大而时间长的操作,只要你不想为等待某个异步操作而阻塞线程执行,那么任何时候都可以使用

同步与异步

JS中同步任务会立即执行并放入调用栈中,而异步任务会被放入事件队列中,等待调用栈中的任务执行完毕后再被推入调用栈中执行。当异步任务被推入调用栈中执行时,它就变成了同步任务。这种机制被称为事件循环。
同步行为对应内存中顺序执行的处理器指令。每条指令都会严格按照它们出现的顺序来执行,而每条指令执行后也能立即获得存储在系统本地(如寄存器或系统内存)的信息。这样的执行流程容易分析程序在执行到代码任意位置时的状态(比如变量的值)。
例如

let x = 3; 
x = x + 4;

在程序执行的每一步,都可以推断出程序的状态。这是因为后面的指令总是在前面的指令完成后才会执行。等到最后一条指定执行完毕,存储在 x 的值就立即可以使用。

异步行为类似于系统中断,即当前进程外部的实体可以触发代码执行。异步代码不容易推断
例如,在定时回调中执行一次简单的数学计算:

let x = 3; 
setTimeout(() => x = x + 4, 1000);

这段程序虽与上面同步代码执行的任务一样,都是把两个数加在一起,但这一次执行线程不知道 x 值何时会改变,因为这取决于回调何时从消息队列出列并执行。虽然这个例子对应的低级代码最终跟前面的例子没什么区别,但第二个指令块(加操作及赋值操作)是由系统计时器触发的,这会生成一个入队执行的中断。到底什么时候会触发这个中断,这对 JavaScript 运行时来说是一个黑盒

异步编程主要包含以下几类,异步编程传统的解决方案是回调函数,这种传统的方式容易导致“回调地狱”,即函数多层嵌套,让代码难以阅读,维护困难,空间复杂度大大增加

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

Promise 对象

Promise属于ES6规范, 是异步编程的一种解决方案,比传统的解决方案——回调函数和事件——更合理和更强大。期约故意将异步行为封装起来,从而隔离外部的同步代码

  1. 从语法上来说: Promise 是一个构造函数
  2. 从功能上来说: promise 对象用来封装一个异步操作并可以获取其成功/失败的结果值
const p=new Promise(()=>{})
console.log(p);

promise及异步编程async await

Promise 的状态及结果

Promise 构造函数: Promise (excutor)

  • executor 函数: 执行器 (resolve, reject) => {}
  • executor 会在 Promise 内部立即同步调用(即executor函数直接放入调用栈中,而不是消息队列,new Promise()执行时就会立即执行executor 函数),异步操作在执行器中执行
let p = new Promise((resolve, reject) => {
    // 同步调用
    console.log(111);
});
console.log(222);

promise及异步编程async await

期约是一个有状态的对象,Promise实例对象中的属性PromiseState存储着该Promise实例的状态,可能处于如下 3 种状态之一:

  • 待定(pending):期约的最初始状态,在待定状态下,期约可以落定(settled)为resolved状态或reject状态。无论落定为哪种状态都是不可逆的。只要从待定转换为解决或拒绝,期约的状态就不再改变。
    例如使用一个空函数对象来应付一下解释器:
let p = new Promise(() => {}); 
setTimeout(console.log, 0, p); // Promise <pending>

之所以说是应付解释器,是因为如果不提供执行器函数,就会抛出 SyntaxError。
无论 resolve()和 reject()中的哪个被调用,状态转换都不可撤销了。于是继续修改状态会静默失败,如下所示:

let p = new Promise((resolve, reject) => { 
 resolve();
 reject(); // 没有效果
});
setTimeout(console.log, 0, p); // Promise <resolved>
  • 解决(resolved,有时候也称为“兑现”,fulfilled):代表成功
  • 拒绝(rejected):代表失败
    无论变为成功还是失败, 都会有一个结果数据(这个数据存储在Promise实例对象的PromiseResult属性中),成功的结果数据一般称为 value, 失败的结果数据一般称为 reason

期约主要有两大用途:

  • 首先是抽象地表示一个异步操作。期约的状态代表期约是否完成。“待定”表示尚未开始或者正在执行中。“解决”表示已经成功完成,而“拒绝”则表示没有成功完成
  • 在另外一些情况下,期约封装的异步操作会实际生成某个值,而程序期待期约状态改变时可以访问这个值。相应地,如果期约被拒绝,程序就会期待期约状态改变时可以拿到拒绝的理由。
    为了支持这两种用例,每个期约只要状态切换为解决,就会有一个私有的内部值(value)。类似地,每个期约只要状态切换为拒绝,就会有一个私有的内部理由(reason)。无论是值还是理由,都是包含原始值或对象的不可修改的引用。二者都是可选的,而且默认值为 undefined。在期约到达某个落定状态时执行的异步代码始终会收到这个值或理由。
    由于期约的状态是私有的,所以只能在内部进行操作。内部操作在期约的执行器函数中完成。执行器函数主要有两项职责:初始化期约的异步行为和控制状态的最终转换。其中,控制期约状态的转换是通过调用它的两个函数参数实现的。这两个函数参数通常都命名为 resolve()和 reject()。调用resolve()会把状态切换为兑现,调用 reject()会把状态切换为拒绝。另外,调用 reject()也会抛出错误
let p1 = new Promise((resolve, reject) => resolve()); 
setTimeout(console.log, 0, p1); // Promise <resolved> 
let p2 = new Promise((resolve, reject) => reject()); 
setTimeout(console.log, 0, p2); // Promise <rejected>
// Uncaught error (in promise)

所谓Promise,简单说就是一个容器,里面保存着某个未来才会结束的事件(通常是一个异步操作)的结果。从语法上说,Promise 是一个对象,从它可以获取异步操作的消息。Promise 提供统一的 API,各种异步操作都可以用同样的方法进行处理
有了Promise对象,就可以将异步操作以同步操作的流程表达出来,避免了层层嵌套的回调函数。此外,Promise对象提供统一的接口,使得控制异步操作更加容易
promise及异步编程async await

Promise方法

Promise.prototype.then()

Promise.prototype.then([onResolved|null],[onRejected|null])是为期约实例添加处理程序的主要方法。
参数:onResolved 处理程序和 onRejected 处理程序。这两个参数都是可选的,如果提供的话,则会在期约分别进入“兑现”和“拒绝”状态时执行。
返回值:一个新的Promise对象

可以指定多个回调,当promise对象变为相应的状态的时候就都会执行

let p = new Promise((resolve, reject) => {
    resolve('OK');
});
///指定回调 - 1
p.then(value => {
    console.log(value);
});
//指定回调 - 2
p.then(value => {
    alert(value);
});

promise及异步编程async await

问:then()返回的Promise对象的状态是如何决定的呢?
① 如果抛出异常, 新 promise 变为 rejected, reason 为抛出的异常

let p = new Promise((resolve, reject) => {
    resolve('ok');
});
//执行 then 方法
let result = p.then(value => {
    console.log(value);
    //1. 抛出错误
    throw '出了问题';
}, reason => {
    console.warn(reason);
});

promise及异步编程async await

② 如果返回的是非 promise 的任意值, 新 promise 变为 resolved, value 为返回的值

let p = new Promise((resolve, reject) => {
    resolve('ok');
});
//执行 then 方法
let result = p.then(value => {
    console.log(value);
	//2. 返回结果是非 Promise 类型的对象
    return 521;
}, reason => {
    console.warn(reason);
});
console.log(result);

promise及异步编程async await

③ 如果返回的是另一个新 promise, 此 promise 的结果就会成为新 promise 的结果

let p = new Promise((resolve, reject) => {
    resolve('ok');
});
//执行 then 方法
let result = p.then(value => {
    console.log(value);
    //3. 返回结果是 Promise 对象
    return new Promise((resolve, reject) => {
        // resolve('success');
        reject('error');
    });
}, reason => {
    console.warn(reason);
});
console.log(result);

promise及异步编程async await

不管调用的是onResolved还是onRejected函数,返回的新promise对象主要由返回值决定(抛出错误的情况除外)

then()的链式调用

问:promise 如何串连多个操作任务?
(1) promise 的 then()返回一个新的 promise, 可以形成 then()的链式调用
(2) 通过 then 的链式调用串连多个同步/异步任务

let p = new Promise((resolve, reject) => {
    setTimeout(() => {
        resolve('OK');
    }, 1000);
});
const result=p.then(value => {
    return new Promise((resolve, reject) => {
        resolve("success");
    });
}).then(value => {
    console.log(value);
}).then(value => {
    console.log(value);
})
console.log(result);

p.then(value => {
return new Promise((resolve, reject) => {
resolve("success");
});
}) // Promise (resolved): 'success'

p.then(value => {
return new Promise((resolve, reject) => {
resolve("success");
});
}).then(value => {
console.log(value);
})// Promise (resolved): undefined 因为没有返回值也没有抛出错误,所以值是undefined, 因为undefined不是Promise对象,所以返回的Promise状态为resolved

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

promise及异步编程async await

中断then()链
方法:在then()的回调函数中返回一个 pendding 状态的 promise 对象。因为pendding状态的promise对象不会触发onResolved()或onRejected()函数

let p = new Promise((resolve, reject) => {
    setTimeout(() => {
        resolve('OK');
    }, 1000);
});
p.then(value => {
    console.log(111);
    //有且只有一个方式
    return new Promise(() => {});// 
}).then(value => {
    console.log(222);
}).then(value => {
    console.log(333);
}).catch(reason => {
    console.warn(reason);
});

promise及异步编程async await

穿透:当没有指定相应Promise状态的回调函数时,就可以跳过执行该then()
练习:分析输出结果
1.

let p = new Promise((resolve, reject) => {
    setTimeout(() => {
        // resolve('OK');
        reject('Err');
    }, 10);
});
p.then(null,reason => {
    console.log(111);
}).then(null,reason => {
    console.log(222);
}).then(null,reason => {
    console.log(333);
}).catch(reason => {
    console.warn(reason);
});

分析:p.then(null,reason => {
console.log(111);
})的返回值是Promise (resolved):undefined, 后面没有对应的onResolved的回调函数,就跳过了执行,所以控制台只输出了111
promise及异步编程async await

let p = new Promise((resolve, reject) => {
    setTimeout(() => {
        resolve('OK');
        // reject('Err');
    }, 10);
});
// console.log(p.then(value => {
//     console.log(111);
// },reason=>{
//     throw '失败啦!';
// })==p.then(value => {
//     console.log(111);
// },reason=>{
//     throw '失败啦!';
// }).then(value => {
//     console.log(222);
// })); // false

console.log(p.then(value => {
    console.log(111);
},reason=>{
    throw '失败啦!';
}).then(value => {
    console.log(222);
}).then(value => {
    console.log(333);
}));

p.then(value => {
    throw '失败啦!';
}).then(value => {
    console.log(222);
}).then(value => {
    console.log(333);
}).catch(reason => {
    console.warn(reason);
});

p.then(value => {
throw '失败啦!';
})返回Promise (rejected): '失败啦!',后面没有对应的onRejected()回调函数,就跳过了执行,所以就直接执行catch()来捕获错误信息
promise及异步编程async await

Promise.prototype.catch()

Promise.prototype.catch(onRejected)
catch()基于then()做了一个单独的封装,只接收rejected状态的回调函数

给Promise对象设置回调函数的方法有Promise.prototype.then()和Promise.prototype.catch(),注意Promise.prototype.catch()只能指定错误的回调函数

Promise.resolve()

静态方法,不是实例方法
期约并非一开始就必须处于待定状态,然后通过执行器函数才能转换为落定状态。通过调用Promise.resolve()静态方法,可以实例化一个解决的期约。
下面两个期约实例实际上是一样的:

let p1 = new Promise((resolve, reject) => resolve()); 
let p2 = Promise.resolve();

参数:成功的数据或 promise 对象
说明: 返回一个成功/失败的 promise 对象

  • 参数为非Promise对象,则返回一个成功的Promise对象,成功的结果为该参数
let p1 = Promise.resolve(521);
console.log(p1);

promise及异步编程async await

let p = Promise.resolve(new Error('foo')); 
setTimeout(console.log, 0, p); // Promise <resolved>: Error: foo
  • 参数为Promise对象时,则返回该Promise对象
    对这个静态方法而言,如果传入的参数本身是一个期约,那它的行为就类似于一个空包装。因此,Promise.resolve()可以说是一个幂等方法
let p = Promise.resolve(7); 
setTimeout(console.log, 0, p === Promise.resolve(p)); // true 
setTimeout(console.log, 0, p === Promise.resolve(Promise.resolve(p))); // true 
// 这个幂等性会保留传入期约的状态:
let p = new Promise(() => {}); 
setTimeout(console.log, 0, p); // Promise <pending> 
setTimeout(console.log, 0, Promise.resolve(p)); // Promise <pending> 
setTimeout(console.log, 0, p === Promise.resolve(p)); // true
let p2 = Promise.resolve(new Promise((resolve, reject) => {
    resolve('OK');
}));
console.log(p2);
p2.catch(reason => {
    console.log(reason);
})

promise及异步编程async await

let p2 = Promise.resolve(new Promise((resolve, reject) => {
    reject('Error');
}));
console.log(p2);
p2.catch(reason => {
    console.log(reason);
})

promise及异步编程async await

Promise.reject()

与 Promise.resolve()类似,Promise.reject()会实例化一个拒绝的期约并抛出一个异步错误(这个错误不能通过 try/catch 捕获,而只能通过拒绝处理程序捕获)。
下面的两个期约实例实际上是一样的:

let p1 = new Promise((resolve, reject) => reject()); 
let p2 = Promise.reject();

参数:失败的理由或 promise 对象
说明: 返回一个失败的 promise 对象

  • 参数为非Promise对象时,返回的失败Promise对象的理由就为该参数值
let p1 = Promise.reject(521);
console.log(p1);

promise及异步编程async await

  • 参数为Promise对象时,返回的失败Promise对象的理由就为该参数值(即失败的理由就是该Promise对象)
let p2 = Promise.reject(new Promise((resolve, reject) => {
    resolve('OK');
}));
console.log(p2);

promise及异步编程async await

Promise.all()

参数: 包含 n 个 promise 的数组
说明: 返回一个新的 promise, 只有所有的 promise 都成功该promise才成功,成功结果为所有promise成功结果组成的数组。只要有一个promise失败了该promise就失败,失败理由为第一个失败的promise的失败理由

  • 当所有promise都成功时,返回一个新的成功的promise,成功结果为所有promise成功结果组成的数组
let p1 = new Promise((resolve, reject) => {
    resolve('OK');
})
let p2 = Promise.resolve('Success');
let p3 = Promise.resolve('Oh Yeah');
const result = Promise.all([p1, p2, p3]);
console.log(result);

promise及异步编程async await

  • 当有失败的promise时该promise就失败,失败理由为第一个失败的promise的失败理由
let p1 = new Promise((resolve, reject) => {
    resolve('OK');
})
let p2 = Promise.reject('Error1');
let p3 = Promise.reject('Error2')
const result = Promise.all([p1, p2, p3]);
console.log(result);

promise及异步编程async await

Promise.race()

通过这种方式,可以检测页面中某个请求是否超时,并输出相关的提示信息。
参数: 包含 n 个 promise 的数组
说明: 返回一个新的 promise, 该promise等于第一个完成的 promise(即第一个确定状态的promise)

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);

promise及异步编程async await

改变Promise对象状态的方式

  1. promise回调函数中改变
let p = new Promise((resolve, reject) => {
    //1. resolve 函数
    // resolve('ok'); // pending   => fulfilled (resolved)
    //2. reject 函数
    // reject("error");// pending  =>  rejected 
    //3. 抛出错误
    // throw '出问题了';// pending  =>  rejected 
});
console.log(p);

promise及异步编程async await
2. 调用实例方法Promise.resolve()或Promise.reject()

基本用法

ES6 规定,Promise对象是一个构造函数,用来生成Promise实例

const promise = new Promise(function(resolve, reject) {
  // ... some code

  if (/* 异步操作成功 */){
    resolve(value);
  } else {
    reject(error);
  }
});

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

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

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

下面是一个抽奖示例

<body>
    <button id="btn">点击抽奖</button>
    <script>
        const btn=document.getElementById("btn")
        btn.onclick=function(){
            // 每次点击按钮创建一个Promise实例对象
            const p =new Promise((resolve,reject)=>{
                setTimeout(()=>{
                    let n =parseInt(Math.random()*101)//取值[1,100]的整数
                    if(n<30){
                        resolve(n)
                    }else{
                        reject(n)
                    }
                },10)
            })
            p.then((value)=>{
                alert("恭喜中奖!中奖数字为"+value);
            },(reason)=>{
                alert("再接再厉! 中奖数字为"+reason);
            })
        }
    </script>
</body>

加载图片资源例子

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>
<body>
    <div></div>
    <script>
        function loadImageAsync(url) {
            var promise = 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;
            });
            return promise;
        }
        loadImageAsync("http://iwenwiki.com/api/vue-data/vue-data-1.png")
        .then(function(data){
            console.log(data);
            $("div").append(data)
        },function(error){
            $("div").html(error)
        })
    </script>
</body>
</html>

实时效果反馈

1. Promise的作用是什么,下列描述正确的是:

A Promise 是异步编程的一种解决方案,可以将异步操作以同步操作的流程表达出来

B Promise是同步编程的一种解决方案,可以将同步操作以异步操作的流程表达出来

C Promise使得控制同步操作更加容易

D Promise还不是ES6的标准,目前是社区版本

答案

1=>A

Promise对象_Ajax实操

Promise封装Ajax,让网络请求的异步操作变得更简单

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>
<body>
    <script>
        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("http://iwenwiki.com/api/blueberrypai/getIndexBanner.php").then(function (json) {
            console.log(json);
        }, function (error) {
            console.error('出错了', error);
        });
    </script>
</body>
</html>

util.promisefy()函数

传入一个遵循常见的错误优先的回调风格的函数(即以(err, value)=>{...}回调作为最后一个参数),并返回一个返回值为promise对象的函数版本
“错误优先”是指回调函数中,错误参数为回调函数的第一个参数。node.js环境中fs模块中的异步api大多是这种风格的
参数:函数
返回值:函数,返回的该函数的返回值是promise对象
将函数promise化的好处:函数promise化之后,函数的返回值就变成了promise对象,这样就可以调用promise的实例方法then()或catch(), 利用它们的链式写法,就能避免回调函数多层嵌套

示例:调用util.promisefy()函数,将fs.readFile()函数promise化

//引入 util 模块
const util = require('util');
//引入 fs 模块
const fs = require('fs');
//promise化fs.readFile()函数
let mineReadFile = util.promisify(fs.readFile);
// promise化之后就可以调用promise实例的then()或catch()方法
mineReadFile('./resource/content.txt').then(value=>{
    console.log(value.toString());
});

封装promisefy函数

在实际应用中,一个函数满足这几个条件,就可以被 promisify 化:

  • 该方法必须包含回调函数
  • 回调函数必须执行
  • 回到函数第一个参数代表 err 信息,第二个参数代表成功返回的结果
// promisefy()返回一个函数,返回的该函数的返回值是Promise对象
// fn指要promise化的函数
const promisefy = (fn) => {
  // ‘...args’表示剩余参数
  return function(...args){
    return new Promise((resolve,reject)=>{
      fn(...args,(err,data)=>{
        if(err) reject(err);
        resolve(data)
      })
    })
  }
}

实例(第14届蓝桥杯省赛第三期模拟题)
题目

下面就请你以 Node.js 中常用的读取文件操作为例,封装一个 Promisefy 函数,将回调形式调用的读取文件方法转换成一个 Promise 的版本。
目录结构如下:
promise及异步编程async await

  • index.js 是需要补充代码的 js 文件。
  • test.md 供读取的示例文件。

请在 index.js 文件中的补全代码,完成 promisefy 函数的封装。将 fs 中的 readFile 方法 promise 化。也就是说 readFileSync 方法执行后,会返回一个 promise,可以调用 then 方法执行成功的回调或失败的回调。
在控制台运行:node index, 此时应打印出 true,即:回调形式的 fs.readFile 方法读取同个文件的结果与 Promise 形式读取结果一致。

参考答案

const fs = require('fs')
const path = require('path')
const textPath = path.join(__dirname, '/test.md')

// 读取示例文件
fs.readFile(textPath, 'utf8', (err, contrast) => {
  // 通过promisefy转化为链式调用
  const readFileSync = promisefy(fs.readFile)

  readFileSync(textPath, 'utf8')
    .then((res) => {
      console.log(res === contrast) // 此处结果预期:true,即promise返回内容与前面读取内容一致
    })
    .catch((err) => {})
})

const promisefy = (fn) => {
  // TODO 此处完成该函数的封装
  // ‘...args’表示剩余参数
  return function(...args){
    return new Promise((resolve,reject)=>{
      fn(...args,(err,data)=>{
        if(err) reject(err);
        resolve(data)
      })
    })
  }
}

module.exports = promisefy // 请勿删除该行代码

封装Promise类实战

promise及异步编程async await

点击查看代码
// 在异步任务中不能抛出错误,即使是内置的Promise也捕获不到

class Promise{
    constructor(executor){
        // 设置默认的PromiseState和PromiseResult
        // 因为Promise()构造函数是以实例的方式调用(new运算符),所以this指向实例
        this.PromiseState='pendding'
        this.PromiseResult=null
        // 将callback对象定义到实例内部,用来存储后面通过then()或catch()指定的onResolved()和onRejected()函数,这是考虑到executor中通过异步任务改变promise实例状态和多个then()指定了多个onResolved()或onRejected()回调的情况
        this.callbacks=[];
        // 而resolve()和reject()是以函数形式调用的,this为window对象,所以这里用self存储指向实例的this
        const self=this;
        function resolve(data){
            // 该判断为了避免状态重复改变
            if(self.PromiseState!=='pendding') return;
            self.PromiseState='fulfilled'//或'resolved'
            self.PromiseResult=data
            // 注意要在Promise实例状态改变并且数据赋值之后调用onResolved
            // 注意在调用前要判断有没有定义onResolved()函数,所以就看callbacks数组第一个元素有没有onResolved属性
            // if(self.callbacks[0].onResolved){
                // 通过setTimeout将then中的回调设置成异步, 因为内置的Promise的实例方法then()中的回调是异步的

                setTimeout(()=>{
                    self.callbacks.forEach(item => {
                        item.onResolved(data)
                    })
                });
            // }
        };
        function reject(data){
            // 该判断为了避免状态重复改变
            if(self.PromiseState!=='pendding') return;
            self.PromiseState='rejected'
            self.PromiseResult=data
            // 注意要在Promise实例状态改变并且数据赋值之后调用onRejected
            // 注意在调用前要判断有没有定义onRejected()函数,所以就看callbacks数组第一个元素有没有onRejected属性
            // if(self.callbacks[0].onRejected){
                // 通过setTimeout将then中的回调设置成异步, 因为内置的Promise的实例方法then()中的回调是异步的

                setTimeout(()=>{
                    self.callbacks.forEach(item => {
                        item.onRejected(data)
                    })
                })
                
            // }
        };
        try{
            // executor在传入时就能确定是个函数,所以这里不需要再定义executor, 只是调用即可
            executor(resolve,reject)
        }catch(e){
            reject(e)
        }
    }

    //添加 then 方法
    then(onResolved, onRejected){
        self=this
        // 考虑异常穿透情况
        if(typeof onRejected!=='function'){
            onRejected=reason=>{
                throw reason;
            }
        }
        if(typeof onResolved!=='function'){
            onResolved=value=>value // (value)=>{return value}的简写形式
        }
        // 
        
        return new Promise((resolve,reject)=>{
            // 这个callback()函数得放到返回的Promise实例里,否则会报错,说resolve和reject未定义
            function callback(type){
                try{
                    // 因为callback()是以函数形式调用的,此时this指向window对象,所以此处参数不能为this.PromiseResult
                    let result=type(self.PromiseResult)
                    if(result instanceof Promise){
                        result.then(v=>{
                            resolve(v)
                        },e=>{
                            reject(e)
                        })
                    }else{// 抛出错误时result是undefined还是null? 不进入该分支应该
                        resolve(result)
                    }
                }catch(e){//考虑抛出错误的情况
                    reject(e)
                }
            }
            // 考虑executor中同步任务改变promise实例状态
            
            if(this.PromiseState==='fulfilled'){
                // 通过setTimeout将then中的回调设置成异步, 因为内置的Promise的实例方法then()中的回调是异步的
                setTimeout(()=>{
                    callback(onResolved)
                })
            }
            
            if(this.PromiseState==='rejected'){
                // 通过setTimeout将then中的回调设置成异步, 因为内置的Promise的实例方法then()中的回调是异步的

                setTimeout(()=>{
                    callback(onRejected)
                })
            }
            // 

            // 考虑executor中异步任务改变promise实例状态
            if(this.PromiseState==='pendding'){
                this.callbacks.push({
                    onResolved:function(){
                        callback(onResolved)
                    },
                    onRejected:function(){
                        callback(onRejected)
                    }
                })
            }
        })
    }
    // 定义Promise.prototype.catch()
    catch(onRejected){
        return this.then(undefined,onRejected)
    }
    // 定义Promise.resolve()静态方法,注意静态方法要加关键词static
    static resolve(value){
        return new Promise((resolve,reject)=>{
            if(value instanceof Promise){
                value.then(
                    v=>resolve(v),
                    e=>reject(e))
            }else{
                resolve(value)
            }
        })
    }
    // 定义Promise.reject()静态方法
    static reject(reason){
        return new Promise((resolve,reject)=>{
            reject(reason)
        })
    }
    // 定义Promise.all()静态方法
    static all(promises){
        let arr=[]
        let count=0;
        return new Promise((resolve,reject)=>{
            for(let i in promises){
                promises[i].then(v=>{
                    arr[i]=v;
                    count++;
                    if(count===promises.length){
                        resolve(arr)
                    }
                },r=>{
                    reject(r)
                })
            }
            // if(count===promises.length){
            //     resolve(arr)
            // }
        })
    }
    // 定义Promise.race()静态方法
    static race(promises){
        return new Promise((resolve,reject)=>{
            for(let i in promises){
                promises[i].then(
                    v=>resolve(v),
                    r=>reject(r))
            }
        })
    }
}

Async 函数

async 英文单词的意思是异步,虽然它是 ES8 中新增加的一个关键字,但它的本质是一种语法糖写法(语法糖是一种简化后的代码写法,它能方便程序员的代码开发),async 通常写在一个函数的前面,表示这是一个异步请求的函数,将返回一个 Promise 对象,并可以通过 then 方法取到函数中的返回值
使用 async 关键字可以让函数具有异步特征,但总体上其代码仍然是同步求值的。而在参数或闭包方面,异步函数仍然具有普通 JavaScript 函数的正常行为。异步函数的返回值会被 Promise.resolve()包装成一个期约对象。异步函数始终返回期约对象。
ES2017 标准引入了 async 函数,使得异步操作变得更加方便
async函数可以将异步操作变为同步操作

  1. 函数的返回值为 promise 对象
  2. promise 对象的结果由 async 函数执行的返回值决定
    • 如果返回值是一个非Promise类型的数据,相当于执行了Promise.resolve(), 则返回的Promise实例状态为fulfilled
    • 如果返回值是一个Promise对象,相当于执行了Promise.resolve(返回值),则返回的Promise实例等效于该Promise对象
async function main(){
    // return 521; // Promise<fulfilled>:521
    return new Promise((resolve, reject) => {
        // resolve('OK'); // Promise<fulfilled>:'OK'
        reject('Error'); // Promise<rejected>:'Error'
    });
    //3. 抛出异常
    // throw "Oh NO"; // Promise<rejected>:"Oh NO"
}
let result = main();
console.log(result);

promise及异步编程async await

示例代码

function print(){
    setTimeout(() =>{
        console.log("定时器");
    },1000)
    console.log("Hello");
}

print()

基本语法

function timeout(ms) {
  return new Promise((resolve) => {
    setTimeout(resolve, ms);
  });
}

async function asyncPrint(value, ms) {
  await timeout(ms);
  console.log(value);
}

asyncPrint('hello world', 50);

异步应用

function ajax(url){
    return new Promise(function(resolve,reject){
        $.getJSON(url,function(result){
            resolve(result)
        },function(error){
            reject(error) 
        })
    })
}

async function getInfo(){
    let ids = await ajax("http://iwenwiki.com/api/generator/list.php")
    let names = await ajax("http://iwenwiki.com/api/generator/id.php?id="+ids[0])
    let infos = await ajax("http://iwenwiki.com/api/generator/name.php?name=" + names.name)
    console.log(infos);
}

getInfo();

实时效果反馈

1. Async 是什么:

A Async是完成网络请求,如Ajax一样

B Async的作用是完成异步网络请求,如Ajax一样

C Async使得异步操作变得更加方便

D Async是新的网络请求解决方案

答案

1=>C

await 表达式

await 可以理解为 async wait 的简写,表示等待异步执行完成。await 后可以返回任意的表达式,如果是正常内容,则直接执行,如果是异步请求,必须等待请求完成后,才会执行下面的代码

  1. await 右侧的表达式一般为 promise 对象, 但也可以是其它的值
  2. 如果表达式是 promise 对象, await 返回的是 promise 成功的值, promise对象状态是rejected时,需要捕获错误来查看错误理由
  3. 如果表达式是其它值, 直接将此值作为 await 的返回值

注意

  1. await 必须写在 async 函数中, 但 async 函数中可以没有 await
  2. 如果 await 的 promise 失败了, 就会抛出异常, 需要通过 try...catch 捕获处理
// 函数 p 返回的是一个 Promise 对象,在对象中,延时 2 秒,执行成功回调函数,相当于模拟一次异步请求
function p(v) {
  return new Promise(function (resolve) {
    setTimeout(function () {
      // 在 p 函数执行时,将函数的实参值 v ,作为执行成功回调函数的返回值。
      resolve(v);
    }, 2000);
  });
}

// 一个用于正常输出内容的函数
function log() {
  console.log("2.正在操作");
}

async function fn() {
  console.log("1.开始");
  await log();
  let p1 = await p("3.异步请求");
  console.log(p1);
  console.log("4.结束");
}
fn();

promise及异步编程async await
根据页面效果,源代码解析如下:

  • fn 函数执行后,首先,会按照代码执行流程,先输出“1.开始”。
  • 其次,对于没有异步请求的内容,在 await 后面都将会正常输出,因此,再输出“2.正在操作”。
  • 如果 await 后面是异步请求,那么,必须等待请求完成并获取结果后,才会向下执行。
  • 根据上述分析,由于 方法 p 是一个异步请求,因此,必须等待它执行完成后,并将返回值赋给变量 p1,再执行向下代码。
  • 所以,最后的执行顺序是,先输出 “3.异步请求”,再输出 "4.结束",在 async 函数中的执行顺序,如下图所示。

promise及异步编程async await

async function main(){
    let p = new Promise((resolve, reject) => {
        // resolve('OK');
        reject('Error');
    })
    //1. 右侧为promise的情况
    // let res = await p;
    //2. 右侧为其他类型的数据, 但这种情况不常见
    // let res2 = await 20;
    //3. 如果promise是失败的状态,需要捕获错误来查看错误理由
    try{
        let res3 = await p;
        console.log(res3);
    }catch(e){
        console.log(e);
    }
}
main();

promise及异步编程async await

async与await结合实践

1.txt

观书有感
       -朱熹
半亩方塘一鉴开
天光云影共徘徊。

2.txt

问渠那得清如许?
为有源头活水来。

3.txt


---------
中华古诗词
const fs = require('fs')
const util= require('util')
// 将fs.readFile promise化,即将异步函数转换成同步的形式(只是转换形式,实质还是异步)
const myReadFile=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/2.txt',(err,data3)=>{
//             if(err) throw err;
//             console.log(data1+data2+data3);
//         })
//     })
// })

// 结合使用async和await, 将异步任务以同步的形式调用,减少嵌套,结构更清晰
async function main(){
    try{
        const data1=await myReadFile('./resource/1.txt')
        const data2=await myReadFile('./resource/2.txt')
        const data3=await myReadFile('./resource/3.txt')
        console.log(data1+data2+data3);
    }catch(e){
        console.log(e);
    }
}
main()

promise及异步编程async await文章来源地址https://www.toymoban.com/news/detail-443973.html

async与await结合发送AJAX请求

//axios是基于Promise封装的AJAX请求库
function sendAJAX(url){
    return new Promise((resolve, reject) => {
        const xhr = new XMLHttpRequest();
        xhr.responseType = 'json';
        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);
                }
            }
        }
    });
}

//段子接口地址 https://api.apiopen.top/getJoke
let btn = document.querySelector('#btn');

btn.addEventListener('click',async function(){
    //获取段子信息
    let duanzi = await sendAJAX('https://api.apiopen.top/getJoke');
    console.log(duanzi);
});

到了这里,关于promise及异步编程async await的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • vue async await promise 等待异步接口执行完毕再进行下一步操作

    需求:上传多个文件,每上传一个文件异步请求一下后台接口,并返回一个新文件名,等把所有的异步接口执行成功后,将上传已成功后新文件名数组得到再去更新业务数据 uni-file-picker 文件上传组件的配置 选择文件后,上传到服务器后端,一个一个的传,等异步执行完一下

    2024年02月12日
    浏览(50)
  • 异步编程的概念 以及async和await的工作原理

    一、引言 二、异步编程的基本概念 三、基于任务的异步模式(TAP) 四、async和await async的工作原理: await的工作原理: 五、异步方法的编写和调用 六、异常处理 七、取消异步操作 八、性能考虑 九、案例:异步下载文件 十、结论 在.NET中,异步编程是一

    2024年04月16日
    浏览(43)
  • 深入理解Async/Await:从原理到实践的JavaScript异步编程指南

    理解 async/await 的原理和使用方法是理解现代JavaScript异步编程的关键。这里我会提供一个详细的实例,涵盖原理、流程、使用方法以及一些注意事项。代码注释会尽量详尽,确保你理解每个步骤。 实例:使用async/await进行异步操作 详细解释和注释: 异步函数定义: async func

    2024年02月05日
    浏览(65)
  • 一盏茶的功夫帮你彻底搞懂JavaScript异步编程从回调地狱到async/await

     🎬 江城开朗的豌豆 :个人主页  🔥  个人专栏   :《 VUE 》 《 javaScript 》 ⛺️  生活的理想,就是为了理想的生活 ! 目录 📘 1. 引言 📘 2. 使用方法 📘 3. 实现原理 📘 4. 写到最后   在深入讨论 async/await 之前,我们需要了解一下 JavaScript 的单线程和非阻塞的特性。

    2024年02月08日
    浏览(51)
  • Unity中的异步编程【5】——在Unity中使用 C#原生的异步(Task,await,async) - System.Threading.Tasks

    1、System.Threading.Tasks中的Task是.Net原生的异步和多线程包。 2、UniTask(Cysharp.Threading.Tasks)是仿照.Net原生的Task,await,async开发的一个包,该包专门服务于Unity,所以取名UnityTask,简称UniTask。 3、既然有Task了,为啥还要搞一个UniTask (1)Task可以用在PC和Android上,但是在WebGL上则会

    2023年04月17日
    浏览(51)
  • Promise、Async/Await 详解

            Promise是抽象异步处理对象以及对其进行各种操作的组件。Promise本身是同步的立即执行函数解决异步回调的问题, 当调用 resolve 或 reject 回调函数进行处理的时候, 是异步操作, 会先执行.then/catch等,当主栈完成后,才会去调用执行resolve/reject中存放的方法。      

    2024年02月14日
    浏览(39)
  • async/await实现Promise.all()

    🐱个人主页: 不叫猫先生 🙋‍♂️作者简介:专注于前端领域各种技术,热衷分享,期待你的关注。 💫系列专栏:vue3从入门到精通 📝个人签名:不破不立 Promise.all() 方法接收一个 promise 的 iterable 类型(注:Array,Map,Set 都属于 ES6 的 iterable 类型)的输入,并且 只返回

    2024年01月18日
    浏览(45)
  • 彻底理解Promise和async/await

    1.异步行为是 为了优化因计算量大而时间长的操作 . 2.pedding 待定: 表示尚未开始或正在进行中    fulfilled 解决: 表示已经成功完成    rejected 拒绝: 表示没有完成 3.从pedding状态切换到fulfilled状态或rejected状态后,状态就不会再改变.而且也不能保证promise比如会脱离待定状态. 因此

    2024年02月03日
    浏览(48)
  • ES6 Promise/Async/Await使用

    Promise应用 在工作中, 我们经常会遇到用异步请求数据, 查询一个结果, 然后把返回的参数放入到下一个执行的异步函数像这样: 当我们使用Promise后, 我们的程序就变成了这样: 控制台输出如下: async/await应用 看是不是简洁很多了, 如果你不想使用这种链式调用, 也可以结合async/

    2024年02月12日
    浏览(44)
  • Promise, Generator, async/await的渐进理解

         作为前端开发者的伙伴们,肯定对Promise,Generator,async/await非常熟悉不过了。Promise绝对是烂记于心,而async/await却让使大伙们感觉到爽(原来异步可以这么简单)。可回头来梳理他们的关联时,你惊讶的发现,他们是如此的密切相关。       我对他们三者之间的关联

    2024年02月08日
    浏览(42)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包