Promise原理讲解 async+await应用(异步回调解决方案)

  • 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 -g
promises-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 程序

原文地址:https://www.cnblogs.com/caijw/p/9757916.html

时间: 2024-11-06 01:25:11

Promise原理讲解 async+await应用(异步回调解决方案)的相关文章

Atitit.&#160;Async&#160;await&#160;优缺点&#160;异步编程的原理and实现&#160;java&#160;c#&#160;php

Atitit. Async await 优缺点 异步编程的原理and实现 java c# php 1. async & await的来源1 2. 异步编程history1 2.1. 线程池 2 2.2. 返回值2 2.3. Semaphore 信号量2 2.4. 线程的异常,主线程可以捕获到么2 3. await并不是针对于async的方法,而是针对async方法所返回给我们的Task,2 4. Java里面的task 跟个 await3 5. ---code4 6. async & aw

[.NET] 利用 async &amp; await 进行异步 IO 操作(整理中...)

利用 async & await 进行异步 IO 操作 可以使用异步函数访问文件.使用异步功能,可以调用异步方法,而不使用回调或拆分您在多个方法或 lambda 表达式中的代码. 若要使同步代码异步,则调用异步方法而不是一个同步方法并添加几个关键字到代码中. 您可能认为添加的以下原因 asynchrony 到文件访问调用: Asynchrony 建议于应用程序的响应能力更强.,因为是一个操作的 UI 线程可以执行其他工作. 如果 UI 线程必须执行需要很长时间的代码(例如,超过 50 毫秒),U

async+await处理异步问题

在编写网页的时候我们常常会遇到异步问题,async+await是es6提出的解决异步的方法,下面我们来看看这个方法怎么实现解决异步的, 大家都知道,setTimeout是一个定时器.他是一个异步执行的语句.如: function foo(){ setTimeout(()=>{ console.log(1) },1000) console.log(2) } 答案肯定是2,1 因为setTimeout是一个异步执行语句,所以下面一个会先执行完后再执行异步. 可是,我们有时候会想让异步先执行完了再执行

ASP.NET WebForm中用async/await实现异步出人意料的简单

1. 在.aspx中添加异步标记 <%@ Page Language="C#" Async="true"%> 2. 在.aspx.cs或者.ascx.cs(用户控件)中添加异步方法 private async Task GetMyPosts() { var posts = await ServiceFactory.BlogPostSevice.GetBlogPostsPagedAsync(); rpPosts.DataSource = posts; rp

[.NET] 利用 async &amp; await 的异步编程

利用 async & await 的异步编程 [博主]反骨仔 [出处]http://www.cnblogs.com/liqingwen/p/5922573.html  目录 异步编程的简介 异步提高响应能力 更容易编写的异步方法 异步方法的控制流 线程 async 和 await 返回类型和参数信息 命名的约定 一.异步编程的简介 通过使用异步编程,你可以避免性能瓶颈并增强应用程序的总体响应能力. Visual Studio 2012 引入了一个简化的方法,异步编程,在 .NET Framewo

js async await 终极异步解决方案

既然有了promise 为什么还要有async await ? 当然是promise 也不是完美的异步解决方案,而 async await 的写法看起来更加简单且容易理解. 回顾 Promise Promise 对象用于表示一个异步操作的最终状态(完成或失败),以及其返回的值. Promise 对象是由关键字 new 及其构造函数来创建的.构造函数会,把一个叫做“处理器函数”(executor function)的函数作为它的参数.这个“处理器函数”接受两个函数resolve 和 reject 

async/await处理异步

async函数返回一个Promise对象,可以使用then方法添加回调函数.当函数执行的时候,一旦遇到await就会先返回,等到异步操作完成,再接着执行函数体内后面的语句. 看代码: 指定多少毫秒后输出一个值 function timeout(ms) { return new Promise((resolve) => { setTimeout(resolve, ms); }); } async function asyncPrint(value, ms) { await timeout(ms);

js异步回调Async/Await与Promise区别 新学习使用Async/Await

Promise,我们了解到promise是ES6为解决异步回调而生,避免出现这种回调地狱,那么为何又需要Async/Await呢?你是不是和我一样对Async/Await感兴趣以及想知道如何使用,下面一起来看看这篇文章:Async/Await替代Promise的6个理由. 什么是Async/Await? async/await是写异步代码的新方式,以前的方法有回调函数和Promise. async/await是基于Promise实现的,它不能用于普通的回调函数. async/await与Prom

node.js异步控制流程 回调,事件,promise和async/await

写这个问题是因为最近看到一些初学者用回调用的不亦乐乎,最后代码左调来又调去很不直观. 首先上结论:推荐使用async/await或者co/yield,其次是promise,再次是事件,回调不要使用. 接下来是解析,为什么我会有这样的结论 首先是回调,理解上最简单,就是我把任务分配出去,当你执行完了我就能从你那里拿到结果执行相应的回调, 这里演示一个对setTimeout的封装,规定时间后打印相应结果并执行回调函数 并且这个函数传给回调函数的参数符合node标准,第一个为error信息,如果出错e