Promise及其使用

Promise及其使用

为何需要Promise

Promise的提出是为了解决异步场景这个领域的问题. 这个章节会首先说明目前JS在处理异步时有哪些问题, 然后给出Promise处理这些问题的方案.

为何需要异步

  • 单线程+事件驱动 (浏览器JS, NodeJs, Redis, Nginx…)

传统的异步处理方案

  • 回调方式 callback
  • 多线程(非JS)

CallBack方案存在的问题

  • 回调金字塔
  • 格式定义不统一(风格)
  • 异步语法(风格)
  • 控制权(控制反转) – 触发次数?触发时机?触发频率?
function wait1000(cb){
     setTimeout(cb, 1000);
}
function wait1000(cb){
     setInterval(cb, 1000);
}
function f1(){console.log('f1');}
wait1000(f1);

callback的调用函数(wait1000)和实现callback的函数(f1)并不在一起, callback的执行依赖于其调用方(wait1000)而不是实现方(f1), 换句话说: 我编写了一个函数,但我无法决定这个函数将被以何种方式、在何时被调用、被调用多少次。这会导致代码的状态并不可靠。

举例:

传入cb给扣款业务
创建订单-开发组1
扣款-开发组2
扣款成功-开发组2
调用cb减库存--开发组1
扣款失败--开发组2

假定开发组1负责创建订单和减少库存, 开发组2负责扣款业务, 扣款业务是异步的, 需要传入回调函数cb给出扣款结果。业务流程是开发组1在创建好订单后将库存减一的动作作为回调传递给开发组2实现的扣款模块。
通常情况下一切正常,但是如果开发组2实现的代码有误,会出现以下几种可能性:

  1. 回调cb正常执行1次
  2. 回调cb没有执行(0次)
  3. 回调cb被执行N次 N>1

作为开发组1, 需要对后面2种场景做处理, 但组织这个逻辑比较繁琐.(变量记录+计时器). 繁琐的原因是要协调两个不同的异步过程,而callback模式下没有给出如何协调两个以上callback的普适方案.

Promise的定义

英文中promise含义是承诺, 一个在未来(异步)兑现的结果.
承诺有两种状态: 进行中, 结束
承诺结果有两种可能性: 成功兑现, 失约

正常结束 fullfill
发生异常 reject
pending
resolved
rejected

Promise的两种状态: pending, settled
settled的两种结果: resolved, rejected

Promise总会有1个结果, 结果不能被改变, 结果不能发生多次.

MDN Promise

定义

function showContent(t){
    console.log('内容:', t);
}
function showError(err){
    console.log('错误信息:', err);
}

//Promise.prototype.constructor
var p = new Promise(function(resovle, reject){
    fs.readFile('/tmp/l.log','utf8', function(err, data){
        if(err){
            reject(err);
        }else{
            resolve(data);
        }
    });
});
p.then(showContent, showError);

console.log(typeof p);
console.log(p);
console.log(p.)

状态

Promise 对象

正常进行
发生异常
创建
resolved
rejected

Promise的简单使用

  • 链式调用 (then/catch 返回的仍然是Promise)
p1.then(()=>p2).then(...)
正常进行
发生异常
正常进行
发生异常
创建
创建
rejected
resolved
rejected
//Promise.prototype.then(onFulfilled, onRejected)
//Promise.prototype.catch(onRejected)
//Promise.prototype.finally(onFinally) --- ES2018

p.then(f1, f2)
p.then(f1)
p.catch(f2)
p.then(f1).catch(f3)
p.catch(f2).finally(f4)

各类Promise方式的API

fetch
node-fetch
fs.promises

fetch('http://127.0.0.1:8080/api/get_user_list')
  .then(x=>x.json())
  .then(x=>console.log(x));

组织多个Promise

组织多个异步操作
串行:

p.then().then().then()...

并行:

//等待所有执行成功
Promise.all([p1, p2, p3, ...]),then()

//等待第一个执行成功
Promise.race([p1, p2, p3, ...]).then()

//异步队列逐个执行
function p(name){
    return new Promise(function(resolve, reject){
        setTimeout(() => {
            console.log('Time: ', new Date(), name);
            resolve(Date.now());
        }, 1000);
    })
}
let tasks = ['A','B','C','D','E','F'];
async function quee(){
    while(tasks.length>0){
        await p(tasks[0]);
        tasks.unshift();
    }
}
quee().then(()=>{
    console.log('END, Time: ', new Date());
});

变种

  • 控制时长的异步操作
    promise+setTimeout
function inTime1000(){
    return new Promise(function(resolve, reject){
        setTimeout(() => {
            reject(null);
        }, 1000);
    })
}

Promise.race([
   inTime1000(),
   fetch('http://127.0.0.1:8080/api/get_user_list').then(x=>x.json())
]).then(data=>{
      console.log(data);
}, err=>{
  console.log(err);
});

包装成常用函数: inTime(p, timeOutSecond)

function inTime(p, timeOutSecond){
function inTimeTimer(second){
    return new Promise(function(resolve, reject){
        setTimeout(() => {
            reject(null);
        }, 1000*second);
    })
}

return Promise.race([inTimeTimer(1.5), p]);
}

//inTime( fetch('http://127.0.0.1:8080/api/get_user_list').then(x=>x.json()), 1.2).then();

基于Promise的其他异步场景处理

function p(name, cb){
        setTimeout(() => {
            console.log('Time: ', new Date(), name);
            cb();
        }, 1000);
}
let tasks = ['A','B','C','D','E','F'];
function next(){
    if(tasks.length>0){
        let t = tasks.shift();
        p(t, function(){
            console.log('剩余:', tasks);
            next();
        });
    }else{
        console.log('END, Time: ', new Date());
    }
}
next();

setTimeout(()=>{
    tasks.push('M');
},2100);

await 同步形式的异步调用, 书写形式上更符合思维.

function p(name){
    return new Promise(function(resolve, reject){
        setTimeout(() => {
            console.log('Time: ', new Date(), name);
            resolve(Date.now());
        }, 1000);
    })
}
let tasks = ['A','B','C','D','E','F'];
async function quee(){
    while(tasks.length>0){
        await p(tasks[0]);
        tasks.shift();
        console.log('剩余:', tasks);
    }
}
quee().then(()=>{
    console.log('END, Time: ', new Date());
});

setTimeout(()=>{
    tasks.push('M');
},2100);

参考库

bluebird
Q
polyfill

你可能感兴趣的:(JavaScript,javascript,js)