使用Promise进行异步编程

1、Promise调用链

在这里插入图片描述

Promise是一个有限状态自动机

Promise在收到既定事件后,会根据预设的规则进行转换状态,转换完后执行相应回调

Promise 拥有3种状态
PENDING		初始状态

FULFILLED	已兑现

REJECTED	已拒绝
转换规则
fulfill /  resolve 	将 PENDING 转换为 FULFILLED	(已敲定)

reject	将 PENDING 转换为 REJECTED (已敲定)

处于已敲定 settled 状态下的 Promise 的状态将不能再变化了
状态转换后所执行的回调
Promise 被拒绝 执行 onRejected 回调函数
即 then 的第二个函数参数 或 catch 的第一个函数参数

Promise 被兑现 执行 onFulFilled 回调函数
即 then 的第一个函数参数

若 then 中无函数参数,则会跳过Promise链的这一环到下一环的Promise中去
事件举例 ajax & Promise

我们用xhr对象去请求一张图片,为了避免繁琐的回调写法(callback hell),我们使用 Promise。

代码如下,这里 Promise 接收的事件便是 xhr 的 load 事件和 error 事件,收到事件后,Promise 的状态会按照业务逻辑与预设规则进行转换,并执行转换后的回调,从而触发整个 Promise 调用链。

function get_img(url) {
    return new Promise(function (resolve, reject) {
        let xhr = new XMLHttpRequest();
        xhr.open("GET", url);
        xhr.responseType = "blob";
        xhr.onload = function() {
            if(xhr.status===200 || xhr.status===304) resolve();
            else reject(new Error("ERROR CODE: " + xhr.statusText));
        };
        xhr.onerror = function(){
            reject(new Error("network error"));
        }
        xhr.send();
    });
}

顺便也总结下 xhr 的使用方式:创、开、配、达、错、发。

2、Promise使用技巧

Promise 的朴素用法是 创建 Promise 对象,配置 Promise 状态转换规则,使得 事件A发生时兑现承诺,事件B发生时拒绝承诺等等,用 then / catch 执行状态转换后的回调,并返回一个新 Promise,再循环执行上述操作,直至处理完所有的事件。

举一个例子,查询一位同学的分数,需要用已知的学号 id 查到同学在的班级 class,再用 class 与 id 按某算法查到同学的座位号 seat,再用座位号 seat 查到我们要的结果分数 score,

伪代码如下

let id = "ID";
let _class = 0, _seat = 0, _score = 0;

let id = "ID";
let _class = 0, _seat = 0, _score = 0;

new Promise(function (resolve, reject) {
    _class = "CLASS";
    console.log(_class)
    let success = true;
    if(success) resolve(_class);
    else reject(err);
})
.then(function (_class){
    new Promise(function(resolve, reject) {
        _seat = "SEAT";
        console.log(_seat);
        let success = false;
        if(success) resolve(_seat);
        else reject(_seat);
    })
})
.then(function (_seat){
    new Promise(function(resolve, reject) {
        _score = "SCORE";
        console.log(_score);
        let success = true;
        if(success) resolve(_score);
        else reject(err);
    })
})
.catch(function(err) {
    throw new Error("network error:" + err);
})
.finally(function() {}); 

3、异步的其他方法

异步的写法除了有Promise之外,还有 事件回调观察者模式 两种写法。

我们拿 node.js 处理输入输出的场景举个例子吧,node 的输入输出是异步的,因此常规的写法是事件回调,如下:

const readline = require("readline");
const R = readline.createInterface({
    input: process.stdin,
    output: process.stdout
});
const LOG = console.log;
function _sort(nums) {
    return nums.sort((a,b) => a-b);
}

// CallBack
R.on("line", function(line) {
    line = line.trim();
    let nums = line.split(" ");
    for(let i=0;i<nums.length;i++) nums[i]-='0';
    let res = _sort(nums);
    LOG(res);
});

callback的写法的问题是需要深层嵌套,形成回调地域,Promise等新语法就是为了解决这个问题的。

接下来是 Promise 的写法:

// 公共代码
const readline = require("readline");
const R = readline.createInterface({
    input: process.stdin,
    output: process.stdout
});
const LOG = console.log;
function _sort(nums) {
    return nums.sort((a,b) => a-b);
}

// Promise

new Promise(function(resolve, reject) {
    R.on("line", function(line) {
        resolve(line.trim());
    });
})
.then(function(line) {
    let nums = line.split(" ");
    for(let i=0;i<nums.length;i++) nums[i] -= '0';
    let res = _sort(nums);
    LOG(res);
})
.catch(function(rejection) {
    LOG("some error happened: " + rejection);
})

Promise的调用链能够保证按我们想要的顺序进行执行程序,我们也可以使用async,await对上述代码改写:

// Ansyc + Await
async function fn() {
    try {
        let line = await new Promise(function(resolve, rejection) {
            R.on("line", function(line) {
                resolve(line.trim());
            });
        });
        let nums = line.split(" ");
        for(let i=0;i<nums.length;i++) nums[i]-='0';
        let res = _sort(nums);
        LOG(res);
    }
    catch(rejection) {
        LOG("some error has happened: " + rejection);
    }
}
fn();

最终,我们用观察者模式实现一遍:

// Observable
const readline = require("readline");
const R = readline.createInterface({
    input: process.stdin,
    output: process.stdout
});
const LOG = console.log;
function _sort(nums) {
    return nums.sort((a,b) => a-b);
}

function Observable() {
    this.cbs = new Map();   // Map: k -> [fn1, fn2, ...]
}

Observable.prototype.add = function(e_name, cb) {
    if(!this.cbs.has(e_name)) this.cbs.set(e_name, []);
    let tmp = this.cbs.get(e_name);
    tmp.push(cb);
}

Observable.prototype.emit = function(e_name, ...res) {
    if(this.cbs.has(e_name)) {
        for(let fn of this.cbs.get(e_name)) {
            if(e_name === "line") {
                console.log(res);
                let [line] = res;
                fn(line);
            }
            else {
                try{
                    fn();
                }catch(e) {
                    LOG("some error has happened: " + e);
                }
            }
        }
    }
}

Observable.prototype.CONSTANTS = {
    LINE : "line",
    HTTP : "http"
};

let observable = new Observable();
R.on("line", function(line) {
    observable.emit(observable.CONSTANTS.LINE, line.trim());
});

(function dosort(line) {
    observable.add("line", function cb(line) {
        let nums = line.split(" ");
        for(let i=0;i<nums.length;i++) nums[i]-='0';
        let res = _sort(nums);
        LOG(res);
    })
})();
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值