乐趣区

JavaScript-异步之Promise摘

写在前面

本文内容及代码均摘取出自廖雪峰老师的 JavaScript 教程。
本文仅作为个人笔记,练习代码使用(所有的代码都需要自己手动敲上几遍),内容上也精简了很多。
相关知识还请阅读原文:Promise – JavaScript 教程

Promise

promise 是异步编程的一种解决方案,相比于传统解决方案 callback 更加的优雅。promise 避免了“回调地狱”的产生。

待实现的需求:
函数执行,1s 后输出 pending,2s 后输出 finished

  • callback 方式:
(function print() {setTimeout(function(){console.log('pending');
        setTimeout(function(){console.log('finished');
        },1000)
    },1000)
})()
  • promise 方式:
function createPromise() {return new Promise(function(resolve,reject){setTimeout(resolve,1000)
    });
}

createPromise().then(function(){console.log('pending');
    return createPromise();}).then(function(){console.log('finished');
})
  • ps:使用 promise 易犯的一个错误
let waitSecond = new Promise(function (resolve,reject){setTimeout(resolve,1000)
});
waitSecond.then(function(){console.log('pending');
    return waitSecond;
}).then(function(){console.log('finished');
})
// 1 秒后,控制台同时输出 'pending' 和 'finished'
// 这是因为同一个 promise 只能改变一次状态

这样来看,似乎 promise 的实现更复杂一些。但若场景再复杂化:请求 test1,把请求结果作为参数去请求 test2,以此类推

function request(url, param, successFun, errorFun) {
    $.ajax({
        type: 'GET',
        url: url,
        param: param,
        async: true,    // 默认为 true, 即异步请求;false 为同步请求
        success: successFun,
        error: errorFun
    });
}
  • callback 方式(分步 ajax 请求)
request('test1.html', '', function(data1) {console.log('第一次请求成功, 这是返回的数据:', data1);
    request('test2.html', data1, function (data2) {console.log('第二次请求成功, 这是返回的数据:', data2);
        request('test3.html', data2, function (data3) {console.log('第三次请求成功, 这是返回的数据:', data3);
            //request... 继续请求
        }, function(error3) {console.log('第三次请求失败, 这是失败信息:', error3);
        });
    }, function(error2) {console.log('第二次请求失败, 这是失败信息:', error2);
    });
}, function(error1) {console.log('第一次请求失败, 这是失败信息:', error1);
});
  • promise 方式(分步 ajax 请求)
function sendRequest(url, param) {return new Promise(function(resolve, reject) {request(url, param, resolve, reject);
    });
}

sendRequest('test1.html', '').then(function(data1) {console.log('第一次请求成功, 这是返回的数据:', data1);
    return sendRequest('test2.html', data1);
}).then(function(data2) {console.log('第二次请求成功, 这是返回的数据:', data2);
    return sendRequest('test3.html', data2);
}).then(function(data3) {console.log('第三次请求成功, 这是返回的数据:', data3);
}).catch(function(error) {
    // 用 catch 捕捉前面的错误
    console.log('sorry, 请求失败了, 这是失败信息:', error);
});

promise 练习(结合 setTimeout、DOM 操作等)

  1. 生成一个 0 - 2 之间的随机数,如果大于 1,则等待相应时间后返回成功,否则返回失败:
// 清除最近一次 log:var logging = document.getElementById('log-text');
while(logging.children.length > 0) {logging.removeChild(logging.children[logging.children.length - 1]);
}
// 输出结果到页面:function log(text) {var p = document.createElement('p');
    p.innerText = text;
    logging.appendChild(p);
}
// 构建 promise 并执行
new Promise((resolve,reject) => {var timeOut = Math.random() * 2;
    log('timeOut:' + timeOut);
    if (timeOut > 1) {
        // 使用常规传参
        setTimeout(resolve,timeOut * 1000,'200 OK');
    } else {
        // 使用匿名函数
        setTimeout(() => {// log('do something');
            reject('setTimeout is:' + timeOut)
        },timeOut * 1000);
        // 可见:使用匿名函数更灵活,比如在 resolve、reject 前调其他方法
    }
}).then((result) => {log('成功:' + result);
}).catch((reason) => {log('失败:' + reason);
})
  1. 串行执行一系列需要异步计算获得结果的任务,比如执行先平方,再求和,再平方:
// 清除最近一次 log:var logging = document.getElementById('log-text');
while(logging.children.length > 0) {logging.removeChild(logging.children[logging.children.length - 1]);
}
// 输出结果到页面:function log(text) {var p = document.createElement('p');
    p.innerText = text;
    logging.appendChild(p);
}
// 返回 promise 的乘法运算
function multiply(input) {return new Promise((resolve,reject) => {log('Call multiply():');
        log('Calculate' + input + 'x' + input);
        setTimeout(resolve,1000,input * input);
    })
} 
// 返回 promise 的加法运算
function sum(input) {return new Promise((resolve,reject) => {log('Call sum():');
        log('Calculate' + input + '+' + input);
        setTimeout(resolve,1000,input + input);
    })
}
new Promise((resolve,reject) => {log('start new Promise...');
    resolve(123);
}).then(multiply)
  .then(sum)
  .then(multiply)
  .then(sum)
  .then((result) => {log('The result is:' + result)
  })
退出移动版